diff options
Diffstat (limited to 'economy.c')
-rw-r--r-- | economy.c | 1738 |
1 files changed, 0 insertions, 1738 deletions
diff --git a/economy.c b/economy.c deleted file mode 100644 index bcd3ac552..000000000 --- a/economy.c +++ /dev/null @@ -1,1738 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "currency.h" -#include "functions.h" -#include "strings.h" // XXX InjectDParam() -#include "table/strings.h" -#include "table/sprites.h" -#include "map.h" -#include "news.h" -#include "player.h" -#include "station.h" -#include "vehicle.h" -#include "window.h" -#include "gfx.h" -#include "command.h" -#include "saveload.h" -#include "economy.h" -#include "industry.h" -#include "town.h" -#include "network/network.h" -#include "sound.h" -#include "engine.h" -#include "network/network_data.h" -#include "variables.h" -#include "vehicle_gui.h" -#include "ai/ai.h" -#include "train.h" -#include "newgrf_engine.h" -#include "newgrf_sound.h" -#include "newgrf_callbacks.h" -#include "unmovable.h" -#include "date.h" - -// Score info -const ScoreInfo _score_info[] = { - { SCORE_VEHICLES, 120, 100 }, - { SCORE_STATIONS, 80, 100 }, - { SCORE_MIN_PROFIT, 10000, 100 }, - { SCORE_MIN_INCOME, 50000, 50 }, - { SCORE_MAX_INCOME, 100000, 100 }, - { SCORE_DELIVERED, 40000, 400 }, - { SCORE_CARGO, 8, 50 }, - { SCORE_MONEY, 10000000, 50 }, - { SCORE_LOAN, 250000, 50 }, - { SCORE_TOTAL, 0, 0 } -}; - -int _score_part[MAX_PLAYERS][NUM_SCORE]; - -int64 CalculateCompanyValue(const Player* p) -{ - PlayerID owner = p->index; - int64 value; - - { - Station *st; - uint num = 0; - - FOR_ALL_STATIONS(st) { - if (st->owner == owner) { - uint facil = st->facilities; - do num += (facil&1); while (facil >>= 1); - } - } - - value = num * _price.station_value * 25; - } - - { - Vehicle *v; - - FOR_ALL_VEHICLES(v) { - if (v->owner != owner) continue; - - if (v->type == VEH_Train || - v->type == VEH_Road || - (v->type == VEH_Aircraft && v->subtype<=2) || - v->type == VEH_Ship) { - value += v->value * 3 >> 1; - } - } - } - - value += p->money64 - p->current_loan; // add real money value - - return max64(value, 1); -} - -// if update is set to true, the economy is updated with this score -// (also the house is updated, should only be true in the on-tick event) -int UpdateCompanyRatingAndValue(Player *p, bool update) -{ - byte owner = p->index; - int score = 0; - - memset(_score_part[owner], 0, sizeof(_score_part[owner])); - -/* Count vehicles */ - { - Vehicle *v; - int32 min_profit = 0; - bool min_profit_first = true; - uint num = 0; - - FOR_ALL_VEHICLES(v) { - if (v->owner != owner) continue; - if ((v->type == VEH_Train && IsFrontEngine(v)) || - v->type == VEH_Road || - (v->type == VEH_Aircraft && v->subtype <= 2) || - v->type == VEH_Ship) { - num++; - if (v->age > 730) { - /* Find the vehicle with the lowest amount of profit */ - if (min_profit_first == true) { - min_profit = v->profit_last_year; - min_profit_first = false; - } else if (min_profit > v->profit_last_year) { - min_profit = v->profit_last_year; - } - } - } - } - - _score_part[owner][SCORE_VEHICLES] = num; - /* Don't allow negative min_profit to show */ - if (min_profit > 0) - _score_part[owner][SCORE_MIN_PROFIT] = min_profit; - } - -/* Count stations */ - { - uint num = 0; - const Station* st; - - FOR_ALL_STATIONS(st) { - if (st->owner == owner) { - int facil = st->facilities; - do num += facil&1; while (facil>>=1); - } - } - _score_part[owner][SCORE_STATIONS] = num; - } - -/* Generate statistics depending on recent income statistics */ - { - const PlayerEconomyEntry* pee; - int numec; - int32 min_income; - int32 max_income; - - numec = min(p->num_valid_stat_ent, 12); - if (numec != 0) { - min_income = 0x7FFFFFFF; - max_income = 0; - pee = p->old_economy; - do { - min_income = min(min_income, pee->income + pee->expenses); - max_income = max(max_income, pee->income + pee->expenses); - } while (++pee,--numec); - - if (min_income > 0) - _score_part[owner][SCORE_MIN_INCOME] = min_income; - - _score_part[owner][SCORE_MAX_INCOME] = max_income; - } - } - -/* Generate score depending on amount of transported cargo */ - { - const PlayerEconomyEntry* pee; - int numec; - uint32 total_delivered; - - numec = min(p->num_valid_stat_ent, 4); - if (numec != 0) { - pee = p->old_economy; - total_delivered = 0; - do { - total_delivered += pee->delivered_cargo; - } while (++pee,--numec); - - _score_part[owner][SCORE_DELIVERED] = total_delivered; - } - } - -/* Generate score for variety of cargo */ - { - uint cargo = p->cargo_types; - uint num = 0; - do num += cargo&1; while (cargo>>=1); - _score_part[owner][SCORE_CARGO] = num; - if (update) p->cargo_types = 0; - } - -/* Generate score for player money */ - { - int32 money = p->player_money; - if (money > 0) { - _score_part[owner][SCORE_MONEY] = money; - } - } - -/* Generate score for loan */ - { - _score_part[owner][SCORE_LOAN] = _score_info[SCORE_LOAN].needed - p->current_loan; - } - - // Now we calculate the score for each item.. - { - int i; - int total_score = 0; - int s; - score = 0; - for (i = 0; i < NUM_SCORE; i++) { - // Skip the total - if (i == SCORE_TOTAL) continue; - // Check the score - s = (_score_part[owner][i] >= _score_info[i].needed) ? - _score_info[i].score : - _score_part[owner][i] * _score_info[i].score / _score_info[i].needed; - if (s < 0) s = 0; - score += s; - total_score += _score_info[i].score; - } - - _score_part[owner][SCORE_TOTAL] = score; - - // We always want the score scaled to SCORE_MAX (1000) - if (total_score != SCORE_MAX) score = score * SCORE_MAX / total_score; - } - - if (update) { - p->old_economy[0].performance_history = score; - UpdateCompanyHQ(p, score); - p->old_economy[0].company_value = CalculateCompanyValue(p); - } - - InvalidateWindow(WC_PERFORMANCE_DETAIL, 0); - return score; -} - -// use PLAYER_SPECTATOR as new_player to delete the player. -void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player) -{ - Town *t; - PlayerID old = _current_player; - _current_player = old_player; - - /* Temporarily increase the player's money, to be sure that - * removing his/her property doesn't fail because of lack of money. - * Not too drastically though, because it could overflow */ - if (new_player == PLAYER_SPECTATOR) { - GetPlayer(old_player)->money64 = MAX_UVALUE(uint64) >>2; // jackpot ;p - UpdatePlayerMoney32(GetPlayer(old_player)); - } - - if (new_player == PLAYER_SPECTATOR) { - Subsidy *s; - - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age >= 12) { - if (GetStation(s->to)->owner == old_player) s->cargo_type = CT_INVALID; - } - } - } - - /* Take care of rating in towns */ - FOR_ALL_TOWNS(t) { - /* If a player takes over, give the ratings to that player. */ - if (new_player != PLAYER_SPECTATOR) { - if (HASBIT(t->have_ratings, old_player)) { - if (HASBIT(t->have_ratings, new_player)) { - // use max of the two ratings. - t->ratings[new_player] = max(t->ratings[new_player], t->ratings[old_player]); - } else { - SETBIT(t->have_ratings, new_player); - t->ratings[new_player] = t->ratings[old_player]; - } - } - } - - /* Reset the ratings for the old player */ - t->ratings[old_player] = 500; - CLRBIT(t->have_ratings, old_player); - } - - { - int num_train = 0; - int num_road = 0; - int num_ship = 0; - int num_aircraft = 0; - Vehicle *v; - - // Determine Ids for the new vehicles - FOR_ALL_VEHICLES(v) { - if (v->owner == new_player) { - switch (v->type) { - case VEH_Train: if (IsFrontEngine(v)) num_train++; break; - case VEH_Road: num_road++; break; - case VEH_Ship: num_ship++; break; - case VEH_Aircraft: if (v->subtype <= 2) num_aircraft++; break; - default: break; - } - } - } - - FOR_ALL_VEHICLES(v) { - if (v->owner == old_player && IS_BYTE_INSIDE(v->type, VEH_Train, VEH_Aircraft + 1)) { - if (new_player == PLAYER_SPECTATOR) { - DeleteWindowById(WC_VEHICLE_VIEW, v->index); - DeleteWindowById(WC_VEHICLE_DETAILS, v->index); - DeleteWindowById(WC_VEHICLE_ORDERS, v->index); - DeleteVehicle(v); - } else { - v->owner = new_player; - if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++; - switch (v->type) { - case VEH_Train: if (IsFrontEngine(v)) v->unitnumber = ++num_train; break; - case VEH_Road: v->unitnumber = ++num_road; break; - case VEH_Ship: v->unitnumber = ++num_ship; break; - case VEH_Aircraft: if (v->subtype <= 2) v->unitnumber = ++num_aircraft; break; - } - } - } - } - } - - // Change ownership of tiles - { - TileIndex tile = 0; - do { - ChangeTileOwner(tile, old_player, new_player); - } while (++tile != MapSize()); - } - - /* Change color of existing windows */ - if (new_player != PLAYER_SPECTATOR) ChangeWindowOwner(old_player, new_player); - - { - Player *p; - uint i; - - /* Check for shares */ - FOR_ALL_PLAYERS(p) { - for (i = 0; i < 4; i++) { - /* 'Sell' the share if this player has any */ - if (p->share_owners[i] == _current_player) { - p->share_owners[i] = PLAYER_SPECTATOR; - } - } - } - p = GetPlayer(_current_player); - /* Sell all the shares that people have on this company */ - for (i = 0; i < 4; i++) - p->share_owners[i] = PLAYER_SPECTATOR; - } - - _current_player = old; - - MarkWholeScreenDirty(); -} - -static void PlayersCheckBankrupt(Player *p) -{ - PlayerID owner; - int64 val; - - // If the player has money again, it does not go bankrupt - if (p->player_money >= 0) { - p->quarters_of_bankrupcy = 0; - return; - } - - p->quarters_of_bankrupcy++; - - owner = p->index; - - switch (p->quarters_of_bankrupcy) { - case 2: - AddNewsItem( (StringID)(owner | NB_BTROUBLE), - NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); - break; - case 3: { - /* XXX - In multiplayer, should we ask other players if it wants to take - over when it is a human company? -- TrueLight */ - if (IsHumanPlayer(owner)) { - AddNewsItem( (StringID)(owner | NB_BTROUBLE), - NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); - break; - } - - // Check if the company has any value.. if not, declare it bankrupt - // right now - val = CalculateCompanyValue(p); - if (val > 0) { - p->bankrupt_value = val; - p->bankrupt_asked = 1 << owner; // Don't ask the owner - p->bankrupt_timeout = 0; - break; - } - // Else, falltrue to case 4... - } - case 4: { - // Close everything the owner has open - DeletePlayerWindows(owner); - -// Show bankrupt news - SetDParam(0, p->name_1); - SetDParam(1, p->name_2); - AddNewsItem( (StringID)(owner | NB_BBANKRUPT), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); - - if (IsHumanPlayer(owner)) { - /* XXX - If we are in offline mode, leave the player playing. Eg. there - * is no THE-END, otherwise mark the player as spectator to make sure - * he/she is no long in control of this company */ - if (!_networking) { - p->bankrupt_asked = 0xFF; - p->bankrupt_timeout = 0x456; - break; - } else if (owner == _local_player) { - _network_playas = PLAYER_SPECTATOR; - SetLocalPlayer(PLAYER_SPECTATOR); - } - -#ifdef ENABLE_NETWORK - /* The server has to handle all administrative issues, for example - * updating and notifying all clients of what has happened */ - if (_network_server) { - const NetworkClientState *cs; - NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX); - - /* The server has just gone belly-up, mark it as spectator */ - if (owner == ci->client_playas) { - ci->client_playas = PLAYER_SPECTATOR; - NetworkUpdateClientInfo(NETWORK_SERVER_INDEX); - } - - /* Find all clients that were in control of this company, - * and mark them as spectator; broadcast this message to everyone */ - FOR_ALL_CLIENTS(cs) { - ci = DEREF_CLIENT_INFO(cs); - if (ci->client_playas == owner) { - ci->client_playas = PLAYER_SPECTATOR; - NetworkUpdateClientInfo(ci->client_index); - } - } - } -#endif /* ENABLE_NETWORK */ - } - - /* Remove the player */ - ChangeOwnershipOfPlayerItems(owner, PLAYER_SPECTATOR); - /* Register the player as not-active */ - p->is_active = false; - - if (!IsHumanPlayer(owner) && (!_networking || _network_server) && _ai.enabled) - AI_PlayerDied(owner); - } - } -} - -void DrawNewsBankrupcy(Window *w) -{ - Player *p; - - DrawNewsBorder(w); - - p = GetPlayer(GB(WP(w,news_d).ni->string_id, 0, 4)); - DrawPlayerFace(p->face, p->player_color, 2, 23); - GfxFillRect(3, 23, 3+91, 23+118, 0x323 | USE_COLORTABLE); - - SetDParam(0, p->president_name_1); - SetDParam(1, p->president_name_2); - - DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94); - - switch (WP(w,news_d).ni->string_id & 0xF0) { - case NB_BTROUBLE: - DrawStringCentered(w->width>>1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, 0); - - SetDParam(0, p->name_1); - SetDParam(1, p->name_2); - - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED, - w->width - 101); - break; - - case NB_BMERGER: { - int32 price; - - DrawStringCentered(w->width>>1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, 0); - COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2); - SetDParam(2, p->name_1); - SetDParam(3, p->name_2); - price = WP(w,news_d).ni->params[2]; - SetDParam(4, price); - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - price==0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR, - w->width - 101); - break; - } - - case NB_BBANKRUPT: - DrawStringCentered(w->width>>1, 1, STR_705C_BANKRUPT, 0); - COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2); - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - STR_705D_HAS_BEEN_CLOSED_DOWN_BY, - w->width - 101); - break; - - case NB_BNEWCOMPANY: - DrawStringCentered(w->width>>1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, 0); - SetDParam(0, p->name_1); - SetDParam(1, p->name_2); - COPY_IN_DPARAM(2,WP(w,news_d).ni->params, 2); - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - STR_705F_STARTS_CONSTRUCTION_NEAR, - w->width - 101); - break; - - default: - NOT_REACHED(); - } -} - -StringID GetNewsStringBankrupcy(const NewsItem *ni) -{ - const Player *p = GetPlayer(GB(ni->string_id, 0, 4)); - - switch (ni->string_id & 0xF0) { - case NB_BTROUBLE: - SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE); - SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED); - SetDParam(2, p->name_1); - SetDParam(3, p->name_2); - return STR_02B6; - case NB_BMERGER: - SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER); - SetDParam(1, STR_705A_HAS_BEEN_SOLD_TO_FOR); - COPY_IN_DPARAM(2,ni->params, 2); - SetDParam(4, p->name_1); - SetDParam(5, p->name_2); - COPY_IN_DPARAM(6,ni->params + 2, 1); - return STR_02B6; - case NB_BBANKRUPT: - SetDParam(0, STR_705C_BANKRUPT); - SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY); - COPY_IN_DPARAM(2,ni->params, 2); - return STR_02B6; - case NB_BNEWCOMPANY: - SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED); - SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR); - SetDParam(2, p->name_1); - SetDParam(3, p->name_2); - COPY_IN_DPARAM(4,ni->params, 2); - return STR_02B6; - default: - NOT_REACHED(); - } - - /* useless, but avoids compiler warning this way */ - return 0; -} - -static void PlayersGenStatistics(void) -{ - Station *st; - Player *p; - - FOR_ALL_STATIONS(st) { - _current_player = st->owner; - SET_EXPENSES_TYPE(EXPENSES_PROPERTY); - SubtractMoneyFromPlayer(_price.station_value >> 1); - } - - if (!HASBIT(1<<0|1<<3|1<<6|1<<9, _cur_month)) - return; - - FOR_ALL_PLAYERS(p) { - if (p->is_active) { - memmove(&p->old_economy[1], &p->old_economy[0], sizeof(p->old_economy) - sizeof(p->old_economy[0])); - p->old_economy[0] = p->cur_economy; - memset(&p->cur_economy, 0, sizeof(p->cur_economy)); - - if (p->num_valid_stat_ent != 24) p->num_valid_stat_ent++; - - UpdateCompanyRatingAndValue(p, true); - PlayersCheckBankrupt(p); - - if (p->block_preview != 0) p->block_preview--; - } - } - - InvalidateWindow(WC_INCOME_GRAPH, 0); - InvalidateWindow(WC_OPERATING_PROFIT, 0); - InvalidateWindow(WC_DELIVERED_CARGO, 0); - InvalidateWindow(WC_PERFORMANCE_HISTORY, 0); - InvalidateWindow(WC_COMPANY_VALUE, 0); - InvalidateWindow(WC_COMPANY_LEAGUE, 0); -} - -static void AddSingleInflation(int32 *value, uint16 *frac, int32 amt) -{ - int64 tmp; - int32 low; - tmp = BIGMULS(*value, amt); - *frac = (uint16)(low = (uint16)tmp + *frac); - *value += (int32)(tmp >> 16) + (low >> 16); -} - -static void AddInflation(void) -{ - int i; - int32 inf = _economy.infl_amount * 54; - - for (i = 0; i != NUM_PRICES; i++) { - AddSingleInflation((int32*)&_price + i, _price_frac + i, inf); - } - - _economy.max_loan_unround += BIGMULUS(_economy.max_loan_unround, inf, 16); - - if (_economy.max_loan + 50000 <= _economy.max_loan_unround) - _economy.max_loan += 50000; - - inf = _economy.infl_amount_pr * 54; - for (i = 0; i != NUM_CARGO; i++) { - AddSingleInflation( - (int32*)_cargo_payment_rates + i, - _cargo_payment_rates_frac + i, - inf - ); - } - - InvalidateWindowClasses(WC_BUILD_VEHICLE); - InvalidateWindowClasses(WC_REPLACE_VEHICLE); - InvalidateWindowClasses(WC_VEHICLE_DETAILS); - InvalidateWindow(WC_PAYMENT_RATES, 0); -} - -static void PlayersPayInterest(void) -{ - const Player* p; - int interest = _economy.interest_rate * 54; - - FOR_ALL_PLAYERS(p) { - if (!p->is_active) continue; - - _current_player = p->index; - SET_EXPENSES_TYPE(EXPENSES_LOAN_INT); - - SubtractMoneyFromPlayer(BIGMULUS(p->current_loan, interest, 16)); - - SET_EXPENSES_TYPE(EXPENSES_OTHER); - SubtractMoneyFromPlayer(_price.station_value >> 2); - } -} - -static void HandleEconomyFluctuations(void) -{ - if (_opt.diff.economy == 0) return; - - if (--_economy.fluct == 0) { - _economy.fluct = -(int)GB(Random(), 0, 2); - AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0); - } else if (_economy.fluct == -12) { - _economy.fluct = GB(Random(), 0, 8) + 312; - AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0); - } -} - -static byte _price_category[NUM_PRICES] = { - 0, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 1, 1, 1, 1, 1, 1, - 2, -}; - -static const int32 _price_base[NUM_PRICES] = { - 100, // station_value - 100, // build_rail - 95, // build_road - 65, // build_signals - 275, // build_bridge - 600, // build_train_depot - 500, // build_road_depot - 700, // build_ship_depot - 450, // build_tunnel - 200, // train_station_track - 180, // train_station_length - 600, // build_airport - 200, // build_bus_station - 200, // build_truck_station - 350, // build_dock - 400000, // build_railvehicle - 2000, // build_railwagon - 700000, // aircraft_base - 14000, // roadveh_base - 65000, // ship_base - 20, // build_trees - 250, // terraform - 20, // clear_1 - 40, // purchase_land - 200, // clear_2 - 500, // clear_3 - 20, // remove_trees - -70, // remove_rail - 10, // remove_signals - 50, // clear_bridge - 80, // remove_train_depot - 80, // remove_road_depot - 90, // remove_ship_depot - 30, // clear_tunnel - 10000, // clear_water - 50, // remove_rail_station - 30, // remove_airport - 50, // remove_bus_station - 50, // remove_truck_station - 55, // remove_dock - 1600, // remove_house - 40, // remove_road - 5600, // running_rail[0] railroad - 5200, // running_rail[1] monorail - 4800, // running_rail[2] maglev - 9600, // aircraft_running - 1600, // roadveh_running - 5600, // ship_running - 1000000, // build_industry -}; - -static byte price_base_multiplier[NUM_PRICES]; - -/** - * Reset changes to the price base multipliers. - */ -void ResetPriceBaseMultipliers(void) -{ - uint i; - - // 8 means no multiplier. - for (i = 0; i < NUM_PRICES; i++) - price_base_multiplier[i] = 8; -} - -/** - * Change a price base by the given factor. - * The price base is altered by factors of two, with an offset of 8. - * NewBaseCost = OldBaseCost * 2^(n-8) - * @param price Index of price base to change. - * @param factor Amount to change by. - */ -void SetPriceBaseMultiplier(uint price, byte factor) -{ - assert(price < NUM_PRICES); - price_base_multiplier[price] = factor; -} - -void StartupEconomy(void) -{ - int i; - - assert(sizeof(_price) == NUM_PRICES * sizeof(int32)); - - for (i = 0; i != NUM_PRICES; i++) { - int32 price = _price_base[i]; - if (_price_category[i] != 0) { - uint mod = _price_category[i] == 1 ? _opt.diff.vehicle_costs : _opt.diff.construction_cost; - if (mod < 1) { - price = price * 3 >> 2; - } else if (mod > 1) { - price = price * 9 >> 3; - } - } - if (price_base_multiplier[i] > 8) { - price <<= price_base_multiplier[i] - 8; - } else { - price >>= 8 - price_base_multiplier[i]; - } - ((int32*)&_price)[i] = price; - _price_frac[i] = 0; - } - - _economy.interest_rate = _opt.diff.initial_interest; - _economy.infl_amount = _opt.diff.initial_interest; - _economy.infl_amount_pr = max(0, _opt.diff.initial_interest - 1); - _economy.max_loan_unround = _economy.max_loan = _opt.diff.max_loan * 1000; - _economy.fluct = GB(Random(), 0, 8) + 168; -} - -Pair SetupSubsidyDecodeParam(const Subsidy* s, bool mode) -{ - TileIndex tile; - TileIndex tile2; - Pair tp; - - /* if mode is false, use the singular form */ - SetDParam(0, _cargoc.names_s[s->cargo_type] + (mode ? 0 : 32)); - - if (s->age < 12) { - if (s->cargo_type != CT_PASSENGERS && s->cargo_type != CT_MAIL) { - SetDParam(1, STR_INDUSTRY); - SetDParam(2, s->from); - tile = GetIndustry(s->from)->xy; - - if (s->cargo_type != CT_GOODS && s->cargo_type != CT_FOOD) { - SetDParam(4, STR_INDUSTRY); - SetDParam(5, s->to); - tile2 = GetIndustry(s->to)->xy; - } else { - SetDParam(4, STR_TOWN); - SetDParam(5, s->to); - tile2 = GetTown(s->to)->xy; - } - } else { - SetDParam(1, STR_TOWN); - SetDParam(2, s->from); - tile = GetTown(s->from)->xy; - - SetDParam(4, STR_TOWN); - SetDParam(5, s->to); - tile2 = GetTown(s->to)->xy; - } - } else { - SetDParam(1, s->from); - tile = GetStation(s->from)->xy; - - SetDParam(2, s->to); - tile2 = GetStation(s->to)->xy; - } - - tp.a = tile; - tp.b = tile2; - - return tp; -} - -void DeleteSubsidyWithTown(TownID index) -{ - Subsidy *s; - - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age < 12 && - (((s->cargo_type == CT_PASSENGERS || s->cargo_type == CT_MAIL) && (index == s->from || index == s->to)) || - ((s->cargo_type == CT_GOODS || s->cargo_type == CT_FOOD) && index == s->to))) { - s->cargo_type = CT_INVALID; - } - } -} - -void DeleteSubsidyWithIndustry(IndustryID index) -{ - Subsidy *s; - - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age < 12 && - s->cargo_type != CT_PASSENGERS && s->cargo_type != CT_MAIL && - (index == s->from || (s->cargo_type != CT_GOODS && s->cargo_type != CT_FOOD && index == s->to))) { - s->cargo_type = CT_INVALID; - } - } -} - -void DeleteSubsidyWithStation(StationID index) -{ - Subsidy *s; - bool dirty = false; - - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age >= 12 && - (s->from == index || s->to == index)) { - s->cargo_type = CT_INVALID; - dirty = true; - } - } - - if (dirty) - InvalidateWindow(WC_SUBSIDIES_LIST, 0); -} - -typedef struct FoundRoute { - uint distance; - CargoID cargo; - void *from; - void *to; -} FoundRoute; - -static void FindSubsidyPassengerRoute(FoundRoute *fr) -{ - Town *from,*to; - - fr->distance = (uint)-1; - - fr->from = from = GetRandomTown(); - if (from == NULL || from->population < 400) return; - - fr->to = to = GetRandomTown(); - if (from == to || to == NULL || to->population < 400 || to->pct_pass_transported > 42) - return; - - fr->distance = DistanceManhattan(from->xy, to->xy); -} - -static void FindSubsidyCargoRoute(FoundRoute *fr) -{ - Industry *i; - int trans, total; - CargoID cargo; - - fr->distance = (uint)-1; - - fr->from = i = GetRandomIndustry(); - if (i == NULL) return; - - // Randomize cargo type - if (Random()&1 && i->produced_cargo[1] != CT_INVALID) { - cargo = i->produced_cargo[1]; - trans = i->pct_transported[1]; - total = i->total_production[1]; - } else { - cargo = i->produced_cargo[0]; - trans = i->pct_transported[0]; - total = i->total_production[0]; - } - - // Quit if no production in this industry - // or if the cargo type is passengers - // or if the pct transported is already large enough - if (total == 0 || trans > 42 || cargo == CT_INVALID || cargo == CT_PASSENGERS) - return; - - fr->cargo = cargo; - - if (cargo == CT_GOODS || cargo == CT_FOOD) { - // The destination is a town - Town *t = GetRandomTown(); - - // Only want big towns - if (t == NULL || t->population < 900) return; - - fr->distance = DistanceManhattan(i->xy, t->xy); - fr->to = t; - } else { - // The destination is an industry - Industry *i2 = GetRandomIndustry(); - - // The industry must accept the cargo - if (i == i2 || i == NULL || - (cargo != i2->accepts_cargo[0] && - cargo != i2->accepts_cargo[1] && - cargo != i2->accepts_cargo[2])) - return; - fr->distance = DistanceManhattan(i->xy, i2->xy); - fr->to = i2; - } -} - -static bool CheckSubsidyDuplicate(Subsidy *s) -{ - const Subsidy* ss; - - for (ss = _subsidies; ss != endof(_subsidies); ss++) { - if (s != ss && - ss->from == s->from && - ss->to == s->to && - ss->cargo_type == s->cargo_type) { - s->cargo_type = CT_INVALID; - return true; - } - } - return false; -} - - -static void SubsidyMonthlyHandler(void) -{ - Subsidy *s; - Pair pair; - Station *st; - uint n; - FoundRoute fr; - bool modified = false; - - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type == CT_INVALID) continue; - - if (s->age == 12-1) { - pair = SetupSubsidyDecodeParam(s, 1); - AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b); - s->cargo_type = CT_INVALID; - modified = true; - } else if (s->age == 2*12-1) { - st = GetStation(s->to); - if (st->owner == _local_player) { - pair = SetupSubsidyDecodeParam(s, 1); - AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b); - } - s->cargo_type = CT_INVALID; - modified = true; - } else { - s->age++; - } - } - - // 25% chance to go on - if (CHANCE16(1,4)) { - // Find a free slot - s = _subsidies; - while (s->cargo_type != CT_INVALID) { - if (++s == endof(_subsidies)) - goto no_add; - } - - n = 1000; - do { - FindSubsidyPassengerRoute(&fr); - if (fr.distance <= 70) { - s->cargo_type = CT_PASSENGERS; - s->from = ((Town*)fr.from)->index; - s->to = ((Town*)fr.to)->index; - goto add_subsidy; - } - FindSubsidyCargoRoute(&fr); - if (fr.distance <= 70) { - s->cargo_type = fr.cargo; - s->from = ((Industry*)fr.from)->index; - s->to = (fr.cargo == CT_GOODS || fr.cargo == CT_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index; - add_subsidy: - if (!CheckSubsidyDuplicate(s)) { - s->age = 0; - pair = SetupSubsidyDecodeParam(s, 0); - AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b); - modified = true; - break; - } - } - } while (n--); - } -no_add:; - if (modified) - InvalidateWindow(WC_SUBSIDIES_LIST, 0); -} - -static const SaveLoad _subsidies_desc[] = { - SLE_VAR(Subsidy, cargo_type, SLE_UINT8), - SLE_VAR(Subsidy, age, SLE_UINT8), - SLE_CONDVAR(Subsidy, from, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - SLE_CONDVAR(Subsidy, from, SLE_UINT16, 5, SL_MAX_VERSION), - SLE_CONDVAR(Subsidy, to, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), - SLE_CONDVAR(Subsidy, to, SLE_UINT16, 5, SL_MAX_VERSION), - SLE_END() -}; - -static void Save_SUBS(void) -{ - int i; - Subsidy *s; - - for (i = 0; i != lengthof(_subsidies); i++) { - s = &_subsidies[i]; - if (s->cargo_type != CT_INVALID) { - SlSetArrayIndex(i); - SlObject(s, _subsidies_desc); - } - } -} - -static void Load_SUBS(void) -{ - int index; - while ((index = SlIterateArray()) != -1) - SlObject(&_subsidies[index], _subsidies_desc); -} - -int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type) -{ - CargoID cargo = cargo_type; - byte f; - - /* zero the distance if it's the bank and very short transport. */ - if (_opt.landscape == LT_NORMAL && cargo == CT_VALUABLES && dist < 10) - dist = 0; - - f = 255; - if (transit_days > _cargoc.transit_days_1[cargo]) { - transit_days -= _cargoc.transit_days_1[cargo]; - f -= transit_days; - - if (transit_days > _cargoc.transit_days_2[cargo]) { - transit_days -= _cargoc.transit_days_2[cargo]; - - if (f < transit_days) { - f = 0; - } else { - f -= transit_days; - } - } - } - if (f < 31) f = 31; - - return BIGMULSS(dist * f * num_pieces, _cargo_payment_rates[cargo], 21); -} - -static void DeliverGoodsToIndustry(TileIndex xy, CargoID cargo_type, int num_pieces) -{ - Industry* best = NULL; - Industry* ind; - uint u; - - // Check if there's an industry close to the station that accepts the cargo - // XXX - Think of something better to - // 1) Only deliver to industries which are withing the catchment radius - // 2) Distribute between industries if more then one is present - u = (_patches.station_spread + 8) * 2; - FOR_ALL_INDUSTRIES(ind) { - uint t; - - if (( cargo_type == ind->accepts_cargo[0] || - cargo_type == ind->accepts_cargo[1] || - cargo_type == ind->accepts_cargo[2] - ) && - ind->produced_cargo[0] != CT_INVALID && - ind->produced_cargo[0] != cargo_type && - (t = DistanceManhattan(ind->xy, xy)) < u) { - u = t; - best = ind; - } - } - - /* Found one? */ - if (best != NULL) { - best->was_cargo_delivered = true; - best->cargo_waiting[0] = min(best->cargo_waiting[0] + num_pieces, 0xFFFF); - } -} - -static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type) -{ - Subsidy *s; - TileIndex xy; - Pair pair; - Player *p; - - // check if there is an already existing subsidy that applies to us - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type == cargo_type && - s->age >= 12 && - s->from == from->index && - s->to == to->index) { - return true; - } - } - - /* check if there's a new subsidy that applies.. */ - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type == cargo_type && s->age < 12) { - /* Check distance from source */ - if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL) { - xy = GetTown(s->from)->xy; - } else { - xy = (GetIndustry(s->from))->xy; - } - if (DistanceMax(xy, from->xy) > 9) continue; - - /* Check distance from dest */ - switch (cargo_type) { - case CT_PASSENGERS: - case CT_MAIL: - case CT_GOODS: - case CT_FOOD: - xy = GetTown(s->to)->xy; - break; - - default: - xy = GetIndustry(s->to)->xy; - break; - } - if (DistanceMax(xy, to->xy) > 9) continue; - - /* Found a subsidy, change the values to indicate that it's in use */ - s->age = 12; - s->from = from->index; - s->to = to->index; - - /* Add a news item */ - pair = SetupSubsidyDecodeParam(s, 0); - InjectDParam(2); - - p = GetPlayer(_current_player); - SetDParam(0, p->name_1); - SetDParam(1, p->name_2); - AddNewsItem( - STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier, - NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), - pair.a, pair.b - ); - - InvalidateWindow(WC_SUBSIDIES_LIST, 0); - return true; - } - } - return false; -} - -static int32 DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, byte days_in_transit) -{ - bool subsidised; - Station *s_from, *s_to; - int32 profit; - - assert(num_pieces > 0); - - // Update player statistics - { - Player *p = GetPlayer(_current_player); - p->cur_economy.delivered_cargo += num_pieces; - SETBIT(p->cargo_types, cargo_type); - } - - // Get station pointers. - s_from = GetStation(source); - s_to = GetStation(dest); - - // Check if a subsidy applies. - subsidised = CheckSubsidised(s_from, s_to, cargo_type); - - // Increase town's counter for some special goods types - if (cargo_type == CT_FOOD) s_to->town->new_act_food += num_pieces; - if (cargo_type == CT_WATER) s_to->town->new_act_water += num_pieces; - - // Give the goods to the industry. - DeliverGoodsToIndustry(s_to->xy, cargo_type, num_pieces); - - // Determine profit - profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(s_from->xy, s_to->xy), days_in_transit, cargo_type); - - // Modify profit if a subsidy is in effect - if (subsidised) { - switch (_opt.diff.subsidy_multiplier) { - case 0: profit += profit >> 1; break; - case 1: profit *= 2; break; - case 2: profit *= 3; break; - default: profit *= 4; break; - } - } - - return profit; -} - -/* - * Returns true if Vehicle v should wait loading because other vehicle is - * already loading the same cargo type - * v = vehicle to load, u = GetFirstInChain(v) - */ -static bool LoadWait(const Vehicle* v, const Vehicle* u) -{ - const Vehicle *w; - const Vehicle *x; - bool has_any_cargo = false; - - if (!(u->current_order.flags & OF_FULL_LOAD)) return false; - - for (w = u; w != NULL; w = w->next) { - if (w->cargo_count != 0) { - if (v->cargo_type == w->cargo_type && - u->last_station_visited == w->cargo_source) { - return false; - } - has_any_cargo = true; - } - } - - FOR_ALL_VEHICLES(x) { - if ((x->type != VEH_Train || IsFrontEngine(x)) && // for all locs - u->last_station_visited == x->last_station_visited && // at the same station - !(x->vehstatus & (VS_STOPPED | VS_CRASHED)) && // not stopped or crashed - x->current_order.type == OT_LOADING && // loading - u != x) { // not itself - bool other_has_any_cargo = false; - bool has_space_for_same_type = false; - bool other_has_same_type = false; - - for (w = x; w != NULL; w = w->next) { - if (w->cargo_count < w->cargo_cap && v->cargo_type == w->cargo_type) { - has_space_for_same_type = true; - } - - if (w->cargo_count != 0) { - if (v->cargo_type == w->cargo_type && - u->last_station_visited == w->cargo_source) { - other_has_same_type = true; - } - other_has_any_cargo = true; - } - } - - if (has_space_for_same_type) { - if (other_has_same_type) return true; - if (other_has_any_cargo && !has_any_cargo) return true; - } - } - } - - return false; -} - -int LoadUnloadVehicle(Vehicle *v, bool just_arrived) -{ - int profit = 0; - int v_profit = 0; //virtual profit for feeder systems - int v_profit_total = 0; - int unloading_time = 20; - Vehicle *u = v; - int result = 0; - StationID last_visited; - Station *st; - int t; - uint count, cap; - PlayerID old_player; - bool completely_empty = true; - byte load_amount; - bool anything_loaded = false; - - assert(v->current_order.type == OT_LOADING); - - v->cur_speed = 0; - - /* Loading can only have finished when all the cargo has been unloaded, and - * there is nothing left to load. It's easier to clear this if the - * conditions haven't been met than attempting to check them all before - * enabling though. */ - SETBIT(v->load_status, LS_LOADING_FINISHED); - - old_player = _current_player; - _current_player = v->owner; - - last_visited = v->last_station_visited; - st = GetStation(last_visited); - - for (; v != NULL; v = v->next) { - GoodsEntry* ge; - load_amount = EngInfo(v->engine_type)->load_amount; - if (_patches.gradual_loading) { - uint16 cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v); - if (cb_load_amount != CALLBACK_FAILED) load_amount = cb_load_amount & 0xFF; - } - - if (v->cargo_cap == 0) continue; - - /* If the vehicle has just arrived, set it to unload. */ - if (just_arrived) SETBIT(v->load_status, LS_CARGO_UNLOADING); - - ge = &st->goods[v->cargo_type]; - count = GB(ge->waiting_acceptance, 0, 12); - - /* unload? */ - if (v->cargo_count != 0 && HASBIT(v->load_status, LS_CARGO_UNLOADING)) { - uint16 amount_unloaded = _patches.gradual_loading ? min(v->cargo_count, load_amount) : v->cargo_count; - - CLRBIT(u->load_status, LS_LOADING_FINISHED); - - if (v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000 && !(u->current_order.flags & OF_TRANSFER)) { - // deliver goods to the station - st->time_since_unload = 0; - - unloading_time += v->cargo_count; /* TTDBUG: bug in original TTD */ - if (just_arrived && !HASBIT(v->load_status, LS_CARGO_PAID_FOR)) { - profit += DeliverGoods(v->cargo_count, v->cargo_type, v->cargo_source, last_visited, v->cargo_days); - SETBIT(v->load_status, LS_CARGO_PAID_FOR); - } - result |= 1; - v->cargo_count -= amount_unloaded; - if (_patches.gradual_loading) continue; - } else if (u->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) { - /* unload goods and let it wait at the station */ - st->time_since_unload = 0; - if (just_arrived && (u->current_order.flags & OF_TRANSFER) && !HASBIT(v->load_status, LS_CARGO_PAID_FOR)) { - v_profit = GetTransportedGoodsIncome( - v->cargo_count, - DistanceManhattan(GetStation(v->cargo_source)->xy, GetStation(last_visited)->xy), - v->cargo_days, - v->cargo_type) * 3 / 2; - - v_profit_total += v_profit; - SETBIT(v->load_status, LS_CARGO_PAID_FOR); - } - - unloading_time += v->cargo_count; - t = GB(ge->waiting_acceptance, 0, 12); - if (t == 0) { - // No goods waiting at station - ge->enroute_time = v->cargo_days; - ge->enroute_from = v->cargo_source; - } else { - // Goods already waiting at station. Set counters to the worst value. - if (v->cargo_days >= ge->enroute_time) - ge->enroute_time = v->cargo_days; - if (last_visited != ge->enroute_from) - ge->enroute_from = v->cargo_source; - } - // Update amount of waiting cargo - SB(ge->waiting_acceptance, 0, 12, min(amount_unloaded + t, 0xFFF)); - - if (u->current_order.flags & OF_TRANSFER) { - ge->feeder_profit += v_profit; - u->profit_this_year += v_profit; - } - result |= 2; - v->cargo_count -= amount_unloaded; - if (_patches.gradual_loading) continue; - } - - if (v->cargo_count != 0) completely_empty = false; - } - - /* The vehicle must have been unloaded because it is either empty, or - * the UNLOADING bit is already clear in v->load_status. */ - CLRBIT(v->load_status, LS_CARGO_UNLOADING); - CLRBIT(v->load_status, LS_CARGO_PAID_FOR); - - /* don't pick up goods that we unloaded */ - if (u->current_order.flags & OF_UNLOAD) continue; - - /* update stats */ - ge->days_since_pickup = 0; - switch (u->type) { - case VEH_Train: t = u->u.rail.cached_max_speed; break; - case VEH_Road: t = u->max_speed / 2; break; - default: t = u->max_speed; break; - } - - // if last speed is 0, we treat that as if no vehicle has ever visited the station. - ge->last_speed = min(t, 255); - ge->last_age = _cur_year - v->build_year; - - // If there's goods waiting at the station, and the vehicle - // has capacity for it, load it on the vehicle. - if (count != 0 && - (cap = v->cargo_cap - v->cargo_count) != 0) { - int cargoshare; - int feeder_profit_share; - - if (v->cargo_count == 0) - TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO); - - /* Skip loading this vehicle if another train/vehicle is already handling - * the same cargo type at this station */ - if (_patches.improved_load && (u->current_order.flags & OF_FULL_LOAD) && LoadWait(v,u)) continue; - - /* TODO: Regarding this, when we do gradual loading, we - * should first unload all vehicles and then start - * loading them. Since this will cause - * VEHICLE_TRIGGER_EMPTY to be called at the time when - * the whole vehicle chain is really totally empty, the - * @completely_empty assignment can then be safely - * removed; that's how TTDPatch behaves too. --pasky */ - completely_empty = false; - anything_loaded = true; - - if (cap > count) cap = count; - if (_patches.gradual_loading) cap = min(cap, load_amount); - if (cap < count) CLRBIT(u->load_status, LS_LOADING_FINISHED); - cargoshare = cap * 10000 / ge->waiting_acceptance; - feeder_profit_share = ge->feeder_profit * cargoshare / 10000; - v->cargo_count += cap; - ge->waiting_acceptance -= cap; - v->profit_this_year -= feeder_profit_share; - ge->feeder_profit -= feeder_profit_share; - unloading_time += cap; - st->time_since_load = 0; - - // And record the source of the cargo, and the days in travel. - v->cargo_source = ge->enroute_from; - v->cargo_days = ge->enroute_time; - result |= 2; - st->last_vehicle_type = v->type; - } - } - - v = u; - - if (_patches.gradual_loading) { - /* The time it takes to load one 'slice' of cargo or passengers depends - * on the vehicle type - the values here are those found in TTDPatch */ - uint gradual_loading_wait_time[] = { 40, 20, 10, 20 }; - - unloading_time = gradual_loading_wait_time[v->type - VEH_Train]; - if (HASBIT(v->load_status, LS_LOADING_FINISHED)) { - if (anything_loaded) { - unloading_time += 20; - } else { - unloading_time = 20; - } - } - } - - if (v_profit_total > 0) { - ShowFeederIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, v_profit_total); - } - - if (v->type == VEH_Train) { - // Each platform tile is worth 2 rail vehicles. - int overhang = v->u.rail.cached_total_length - GetStationPlatforms(st, v->tile) * TILE_SIZE; - if (overhang > 0) { - unloading_time <<= 1; - unloading_time += (overhang * unloading_time) / 8; - } - } - - v->load_unload_time_rem = unloading_time; - - if (completely_empty) { - TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY); - } - - if (result != 0) { - InvalidateWindow(WC_VEHICLE_DETAILS, v->index); - MarkStationTilesDirty(st); - - if (result & 2) InvalidateWindow(WC_STATION_VIEW, last_visited); - - if (profit != 0) { - v->profit_this_year += profit; - SubtractMoneyFromPlayer(-profit); - - if (IsLocalPlayer() && !PlayVehicleSound(v, VSE_LOAD_UNLOAD)) { - SndPlayVehicleFx(SND_14_CASHTILL, v); - } - - ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, -profit); - } - } - - _current_player = old_player; - return result; -} - -void PlayersMonthlyLoop(void) -{ - PlayersGenStatistics(); - if (_patches.inflation && _cur_year < MAX_YEAR) - AddInflation(); - PlayersPayInterest(); - // Reset the _current_player flag - _current_player = OWNER_NONE; - HandleEconomyFluctuations(); - SubsidyMonthlyHandler(); -} - -static void DoAcquireCompany(Player *p) -{ - Player *owner; - int i,pi; - int64 value; - - SetDParam(0, p->name_1); - SetDParam(1, p->name_2); - SetDParam(2, p->bankrupt_value); - AddNewsItem( (StringID)(_current_player | NB_BMERGER), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); - - // original code does this a little bit differently - pi = p->index; - ChangeOwnershipOfPlayerItems(pi, _current_player); - - if (p->bankrupt_value == 0) { - owner = GetPlayer(_current_player); - owner->current_loan += p->current_loan; - } - - value = CalculateCompanyValue(p) >> 2; - for (i = 0; i != 4; i++) { - if (p->share_owners[i] != PLAYER_SPECTATOR) { - owner = GetPlayer(p->share_owners[i]); - owner->money64 += value; - owner->yearly_expenses[0][EXPENSES_OTHER] += value; - UpdatePlayerMoney32(owner); - } - } - - p->is_active = false; - - DeletePlayerWindows(pi); - RebuildVehicleLists(); //Updates the open windows to add the newly acquired vehicles to the lists -} - -extern int GetAmountOwnedBy(Player *p, byte owner); - -/** Acquire shares in an opposing company. - * @param tile unused - * @param p1 player to buy the shares from - * @param p2 unused - */ -int32 CmdBuyShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - Player *p; - int64 cost; - - /* Check if buying shares is allowed (protection against modified clients */ - if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares) return CMD_ERROR; - - SET_EXPENSES_TYPE(EXPENSES_OTHER); - p = GetPlayer(p1); - - /* Protect new companies from hostile takeovers */ - if (_cur_year - p->inaugurated_year < 6) return_cmd_error(STR_7080_PROTECTED); - - /* Those lines are here for network-protection (clients can be slow) */ - if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0) return 0; - - /* We can not buy out a real player (temporarily). TODO: well, enable it obviously */ - if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) return 0; - - cost = CalculateCompanyValue(p) >> 2; - if (flags & DC_EXEC) { - PlayerID* b = p->share_owners; - int i; - - while (*b != PLAYER_SPECTATOR) b++; /* share owners is guaranteed to contain at least one PLAYER_SPECTATOR */ - *b = _current_player; - - for (i = 0; p->share_owners[i] == _current_player;) { - if (++i == 4) { - p->bankrupt_value = 0; - DoAcquireCompany(p); - break; - } - } - InvalidateWindow(WC_COMPANY, p1); - } - return cost; -} - -/** Sell shares in an opposing company. - * @param tile unused - * @param p1 player to sell the shares from - * @param p2 unused - */ -int32 CmdSellShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - Player *p; - int64 cost; - - /* Check if buying shares is allowed (protection against modified clients */ - if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares) return CMD_ERROR; - - SET_EXPENSES_TYPE(EXPENSES_OTHER); - p = GetPlayer(p1); - - /* Those lines are here for network-protection (clients can be slow) */ - if (GetAmountOwnedBy(p, _current_player) == 0) return 0; - - /* adjust it a little to make it less profitable to sell and buy */ - cost = CalculateCompanyValue(p) >> 2; - cost = -(cost - (cost >> 7)); - - if (flags & DC_EXEC) { - PlayerID* b = p->share_owners; - while (*b != _current_player) b++; /* share owners is guaranteed to contain player */ - *b = PLAYER_SPECTATOR; - InvalidateWindow(WC_COMPANY, p1); - } - return cost; -} - -/** Buy up another company. - * When a competing company is gone bankrupt you get the chance to purchase - * that company. - * @todo currently this only works for AI players - * @param tile unused - * @param p1 player/company to buy up - * @param p2 unused - */ -int32 CmdBuyCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - Player *p; - - /* Disable takeovers in multiplayer games */ - if (!IsValidPlayer((PlayerID)p1) || _networking) return CMD_ERROR; - - SET_EXPENSES_TYPE(EXPENSES_OTHER); - p = GetPlayer(p1); - - if (!p->is_ai) return CMD_ERROR; - - if (flags & DC_EXEC) { - DoAcquireCompany(p); - } - return p->bankrupt_value; -} - -// Prices -static void SaveLoad_PRIC(void) -{ - SlArray(&_price, NUM_PRICES, SLE_INT32); - SlArray(&_price_frac, NUM_PRICES, SLE_UINT16); -} - -// Cargo payment rates -static void SaveLoad_CAPR(void) -{ - SlArray(&_cargo_payment_rates, NUM_CARGO, SLE_INT32); - SlArray(&_cargo_payment_rates_frac, NUM_CARGO, SLE_UINT16); -} - -static const SaveLoad _economy_desc[] = { - SLE_VAR(Economy, max_loan, SLE_INT32), - SLE_VAR(Economy, max_loan_unround, SLE_INT32), - SLE_VAR(Economy, fluct, SLE_FILE_I16 | SLE_VAR_I32), - SLE_VAR(Economy, interest_rate, SLE_UINT8), - SLE_VAR(Economy, infl_amount, SLE_UINT8), - SLE_VAR(Economy, infl_amount_pr, SLE_UINT8), - SLE_END() -}; - -// Economy variables -static void SaveLoad_ECMY(void) -{ - SlObject(&_economy, _economy_desc); -} - -const ChunkHandler _economy_chunk_handlers[] = { - { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH}, - { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH}, - { 'SUBS', Save_SUBS, Load_SUBS, CH_ARRAY}, - { 'ECMY', SaveLoad_ECMY, SaveLoad_ECMY, CH_RIFF | CH_LAST}, -}; |