tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@2163: #include "functions.h" tron@1309: #include "strings.h" // XXX InjectDParam() tron@507: #include "table/strings.h" celestar@2218: #include "table/sprites.h" tron@679: #include "map.h" truelight@0: #include "news.h" truelight@0: #include "player.h" truelight@0: #include "station.h" truelight@0: #include "vehicle.h" truelight@0: #include "window.h" truelight@0: #include "gfx.h" truelight@0: #include "command.h" truelight@0: #include "saveload.h" truelight@0: #include "economy.h" truelight@0: #include "industry.h" truelight@0: #include "town.h" signde@239: #include "network.h" tron@337: #include "sound.h" tron@445: #include "engine.h" truelight@543: #include "network_data.h" tron@2159: #include "variables.h" tron@2159: #include "vehicle_gui.h" truelight@0: ludde@2261: // Score info ludde@2261: const ScoreInfo _score_info[] = { ludde@2261: {SCORE_VEHICLES, 120, 100}, ludde@2261: {SCORE_STATIONS, 80, 100}, ludde@2261: {SCORE_MIN_PROFIT, 10000, 100}, ludde@2261: {SCORE_MIN_INCOME, 50000, 50}, ludde@2261: {SCORE_MAX_INCOME, 100000, 100}, ludde@2261: {SCORE_DELIVERED, 40000, 400}, ludde@2261: {SCORE_CARGO, 8, 50}, ludde@2261: {SCORE_MONEY, 10000000, 50}, ludde@2261: {SCORE_LOAN, 250000, 50}, ludde@2261: {SCORE_TOTAL, 0, 0} ludde@2261: }; ludde@2261: ludde@2261: int _score_part[MAX_PLAYERS][NUM_SCORE]; ludde@2261: ludde@2261: dominik@762: // get a mask of the allowed currencies depending on the year dominik@768: uint GetMaskOfAllowedCurrencies(void) dominik@762: { dominik@762: int i; dominik@762: uint mask = 0; dominik@768: for (i = 0; i != lengthof(_currency_specs); i++) { dominik@762: uint16 to_euro = _currency_specs[i].to_euro; dominik@762: if (i == 23) mask |= (1 << 23); // always allow custom currency darkvater@970: if (to_euro != CF_NOEURO && to_euro != CF_ISEURO && _cur_year >= (to_euro-MAX_YEAR_BEGIN_REAL)) continue; darkvater@970: if (_cur_year < (2000-MAX_YEAR_BEGIN_REAL) && (to_euro == CF_ISEURO)) continue; dominik@762: mask |= (1 << i); dominik@762: } dominik@762: return mask; dominik@762: } dominik@762: dominik@768: void CheckSwitchToEuro(void) dominik@762: { dominik@762: if (_currency_specs[_opt.currency].to_euro != CF_NOEURO && dominik@762: _currency_specs[_opt.currency].to_euro != CF_ISEURO && darkvater@970: _cur_year >= (_currency_specs[_opt.currency].to_euro-MAX_YEAR_BEGIN_REAL)) { dominik@762: _opt.currency = 2; // this is the index of euro above. dominik@762: AddNewsItem(STR_EURO_INTRODUCE, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0); dominik@762: } dominik@762: } dominik@762: darkvater@147: void UpdatePlayerHouse(Player *p, uint score) truelight@0: { truelight@0: byte val; tron@1977: TileIndex tile = p->location_of_house; truelight@0: truelight@0: if (tile == 0) truelight@0: return; truelight@0: truelight@0: (val = 128, score < 170) || truelight@0: (val+= 4, score < 350) || truelight@0: (val+= 4, score < 520) || truelight@0: (val+= 4, score < 720) || truelight@0: (val+= 4, true); truelight@0: truelight@0: /* house is already big enough */ tron@2049: if (val <= _m[tile].m5) truelight@0: return; truelight@0: tron@2049: _m[tile + TileDiffXY(0, 0)].m5 = val; tron@2049: _m[tile + TileDiffXY(0, 1)].m5 = ++val; tron@2049: _m[tile + TileDiffXY(1, 0)].m5 = ++val; tron@2049: _m[tile + TileDiffXY(1, 1)].m5 = ++val; truelight@0: tron@1981: MarkTileDirtyByTile(tile + TileDiffXY(0, 0)); tron@1981: MarkTileDirtyByTile(tile + TileDiffXY(0, 1)); tron@1981: MarkTileDirtyByTile(tile + TileDiffXY(1, 0)); tron@1981: MarkTileDirtyByTile(tile + TileDiffXY(1, 1)); truelight@0: } truelight@0: truelight@200: int64 CalculateCompanyValue(Player *p) { truelight@0: byte owner = p->index; truelight@200: int64 value; truelight@193: truelight@0: { truelight@0: Station *st; truelight@0: uint num = 0; truelight@193: truelight@0: FOR_ALL_STATIONS(st) { truelight@0: if (st->xy != 0 && st->owner == owner) { truelight@0: uint facil = st->facilities; truelight@0: do num += (facil&1); while (facil >>= 1); truelight@0: } truelight@0: } truelight@0: truelight@0: value = num * _price.station_value * 25; truelight@0: } truelight@0: truelight@0: { truelight@0: Vehicle *v; truelight@0: truelight@0: FOR_ALL_VEHICLES(v) { truelight@0: if (v->owner != owner) truelight@0: continue; truelight@0: if (v->type == VEH_Train || truelight@0: v->type == VEH_Road || truelight@0: (v->type == VEH_Aircraft && v->subtype<=2) || truelight@0: v->type == VEH_Ship) { truelight@0: value += v->value * 3 >> 1; truelight@0: } truelight@0: } truelight@0: } truelight@0: celestar@997: value += p->money64 - p->current_loan; // add real money value tron@1019: Darkvater@1651: return max64(value, 1); truelight@0: } truelight@0: dominik@116: // if update is set to true, the economy is updated with this score dominik@116: // (also the house is updated, should only be true in the on-tick event) darkvater@147: int UpdateCompanyRatingAndValue(Player *p, bool update) truelight@0: { truelight@0: byte owner = p->index; dominik@116: int score = 0; dominik@116: dominik@116: memset(_score_part[owner], 0, sizeof(_score_part[owner])); truelight@0: truelight@0: /* Count vehicles */ truelight@0: { truelight@0: Vehicle *v; ludde@2261: int32 min_profit = _score_info[SCORE_MIN_PROFIT].needed; truelight@0: uint num = 0; truelight@0: truelight@0: FOR_ALL_VEHICLES(v) { truelight@0: if (v->owner != owner) truelight@0: continue; bjarni@1067: if ((v->type == VEH_Train && v->subtype == TS_Front_Engine) || truelight@0: v->type == VEH_Road || truelight@0: (v->type == VEH_Aircraft && v->subtype<=2) || truelight@0: v->type == VEH_Ship) { truelight@0: num++; truelight@0: if (v->age > 730) { dominik@116: if (min_profit > v->profit_last_year) truelight@0: min_profit = v->profit_last_year; truelight@0: } truelight@0: } truelight@0: } truelight@0: dominik@116: _score_part[owner][SCORE_VEHICLES] = num; tron@1407: if (min_profit > 0) tron@1407: _score_part[owner][SCORE_MIN_PROFIT] = min_profit; truelight@0: } truelight@0: truelight@0: /* Count stations */ truelight@0: { truelight@0: uint num = 0; truelight@0: Station *st; truelight@0: truelight@0: FOR_ALL_STATIONS(st) { truelight@0: if (st->xy != 0 && st->owner == owner) { truelight@0: int facil = st->facilities; truelight@0: do num += facil&1; while (facil>>=1); truelight@0: } truelight@0: } dominik@116: _score_part[owner][SCORE_STATIONS] = num; truelight@0: } truelight@0: truelight@0: /* Generate statistics depending on recent income statistics */ truelight@0: { truelight@0: PlayerEconomyEntry *pee; truelight@0: int numec; truelight@0: int32 min_income; truelight@543: int32 max_income; truelight@0: truelight@0: numec = min(p->num_valid_stat_ent, 12); truelight@0: if (numec != 0) { truelight@0: min_income = 0x7FFFFFFF; truelight@0: max_income = 0; truelight@0: pee = p->old_economy; truelight@0: do { truelight@0: min_income = min(min_income, pee->income + pee->expenses); truelight@0: max_income = max(max_income, pee->income + pee->expenses); truelight@0: } while (++pee,--numec); truelight@0: truelight@0: if (min_income > 0) dominik@116: _score_part[owner][SCORE_MIN_INCOME] = min_income; truelight@0: dominik@116: _score_part[owner][SCORE_MAX_INCOME] = max_income; truelight@0: } truelight@0: } truelight@0: truelight@0: /* Generate score depending on amount of transported cargo */ truelight@0: { truelight@0: PlayerEconomyEntry *pee; truelight@0: int numec; truelight@0: uint32 total_delivered; truelight@0: truelight@0: numec = min(p->num_valid_stat_ent, 4); truelight@0: if (numec != 0) { truelight@0: pee = p->old_economy; truelight@0: total_delivered = 0; truelight@0: do { truelight@0: total_delivered += pee->delivered_cargo; truelight@0: } while (++pee,--numec); truelight@0: dominik@116: _score_part[owner][SCORE_DELIVERED] = total_delivered; truelight@0: } truelight@0: } dominik@116: truelight@0: /* Generate score for variety of cargo */ truelight@0: { truelight@0: uint cargo = p->cargo_types; truelight@0: uint num = 0; truelight@0: do num += cargo&1; while (cargo>>=1); dominik@116: _score_part[owner][SCORE_CARGO] = num; dominik@116: if (update) dominik@116: p->cargo_types = 0; truelight@0: } truelight@0: truelight@0: /* Generate score for player money */ truelight@0: { truelight@0: int32 money = p->player_money; truelight@0: if (money > 0) { dominik@116: _score_part[owner][SCORE_MONEY] = money; truelight@0: } truelight@0: } truelight@0: truelight@0: /* Generate score for loan */ truelight@0: { ludde@2261: _score_part[owner][SCORE_LOAN] = _score_info[SCORE_LOAN].needed - p->current_loan; dominik@116: } truelight@193: dominik@116: // Now we calculate the score for each item.. dominik@116: { dominik@116: int i; dominik@116: int total_score = 0; dominik@116: int s; dominik@116: score = 0; dominik@116: for (i=0;i= _score_info[i].needed) ? ludde@2261: _score_info[i].score : ludde@2261: ((_score_part[owner][i] * _score_info[i].score) / _score_info[i].needed); dominik@116: if (s < 0) s = 0; dominik@116: score += s; ludde@2261: total_score += _score_info[i].score; dominik@116: } truelight@193: dominik@116: _score_part[owner][SCORE_TOTAL] = score; truelight@193: dominik@116: // We always want the score scaled to SCORE_MAX (1000) dominik@116: if (total_score != SCORE_MAX) dominik@116: score = score * SCORE_MAX / total_score; truelight@0: } truelight@0: dominik@116: if (update) { dominik@116: p->old_economy[0].performance_history = score; dominik@116: UpdatePlayerHouse(p, score); dominik@116: p->old_economy[0].company_value = CalculateCompanyValue(p); dominik@116: } truelight@193: dominik@116: InvalidateWindow(WC_PERFORMANCE_DETAIL, 0); darkvater@147: return score; truelight@0: } truelight@0: truelight@0: // use 255 as new_player to delete the player. Darkvater@1797: void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player) truelight@0: { Darkvater@1797: PlayerID old = _current_player; truelight@0: _current_player = old_player; truelight@0: Darkvater@1797: if (new_player == OWNER_SPECTATOR) { truelight@0: Subsidy *s; truelight@193: Darkvater@1797: for (s = _subsidies; s != endof(_subsidies); s++) { truelight@0: if (s->cargo_type != 0xff && s->age >= 12) { Darkvater@1797: if (GetStation(s->to)->owner == old_player) truelight@0: s->cargo_type = 0xff; truelight@0: } truelight@0: } truelight@0: } truelight@0: Darkvater@1797: /* Take care of rating in towns */ Darkvater@1797: { Town *t; Darkvater@1797: if (new_player != OWNER_SPECTATOR) { Darkvater@1797: FOR_ALL_TOWNS(t) { Darkvater@1797: /* If a player takes over, give the ratings to that player. */ Darkvater@1797: if (IsValidTown(t) && HASBIT(t->have_ratings, old_player)) { Darkvater@1797: if (HASBIT(t->have_ratings, new_player)) { Darkvater@1797: // use max of the two ratings. Darkvater@1797: t->ratings[new_player] = max(t->ratings[new_player], t->ratings[old_player]); Darkvater@1797: } else { Darkvater@1797: SETBIT(t->have_ratings, new_player); Darkvater@1797: t->ratings[new_player] = t->ratings[old_player]; Darkvater@1797: } Darkvater@1797: } truelight@193: Darkvater@1797: /* Reset ratings for the town */ Darkvater@1797: if (IsValidTown(t)) { Darkvater@1797: t->ratings[old_player] = 500; Darkvater@1797: CLRBIT(t->have_ratings, old_player); truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@193: truelight@0: { truelight@0: int num_train = 0; truelight@0: int num_road = 0; truelight@0: int num_ship = 0; truelight@0: int num_aircraft = 0; truelight@0: Vehicle *v; truelight@0: truelight@0: // Determine Ids for the new vehicles truelight@0: FOR_ALL_VEHICLES(v) { truelight@0: if (v->owner == new_player) { Darkvater@1797: switch (v->type) { Darkvater@1797: case VEH_Train: Darkvater@1797: if (v->subtype == TS_Front_Engine) num_train++; Darkvater@1797: break; Darkvater@1797: case VEH_Road: Darkvater@1797: num_road++; Darkvater@1797: break; Darkvater@1797: case VEH_Ship: Darkvater@1797: num_ship++; Darkvater@1797: break; Darkvater@1797: case VEH_Aircraft: Darkvater@1797: if (v->subtype <= 2) num_aircraft++; Darkvater@1797: break; Darkvater@1797: default: break; Darkvater@1797: } truelight@0: } truelight@0: } truelight@0: truelight@0: FOR_ALL_VEHICLES(v) { truelight@0: if (v->owner == old_player && IS_BYTE_INSIDE(v->type, VEH_Train, VEH_Aircraft+1) ) { Darkvater@1797: if (new_player == OWNER_SPECTATOR) { truelight@0: DeleteWindowById(WC_VEHICLE_VIEW, v->index); truelight@0: DeleteWindowById(WC_VEHICLE_DETAILS, v->index); truelight@0: DeleteWindowById(WC_VEHICLE_ORDERS, v->index); truelight@0: DeleteVehicle(v); truelight@0: } else { truelight@0: v->owner = new_player; bjarni@1067: if (v->type == VEH_Train && v->subtype == TS_Front_Engine) truelight@0: v->unitnumber = ++num_train; truelight@0: else if (v->type == VEH_Road) truelight@0: v->unitnumber = ++num_road; truelight@0: else if (v->type == VEH_Ship) truelight@0: v->unitnumber = ++num_ship; truelight@0: else if (v->type == VEH_Aircraft && v->subtype <= 2) truelight@0: v->unitnumber = ++num_aircraft; truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: // Change ownership of tiles truelight@0: { Darkvater@1797: TileIndex tile = 0; truelight@0: do { truelight@0: ChangeTileOwner(tile, old_player, new_player); tron@863: } while (++tile != MapSize()); truelight@0: } truelight@0: truelight@0: // Change color of existing windows Darkvater@1797: if (new_player != OWNER_SPECTATOR) { truelight@0: Window *w; Darkvater@1797: for (w = _windows; w != _last_window; w++) { truelight@0: if (w->caption_color == old_player) truelight@0: w->caption_color = new_player; truelight@0: } truelight@0: } truelight@0: truelight@780: { truelight@780: Player *p; truelight@780: uint i; truelight@780: truelight@780: /* Check for shares */ truelight@780: FOR_ALL_PLAYERS(p) { Darkvater@1797: for (i = 0; i < 4; i++) { truelight@780: /* 'Sell' the share if this player has any */ truelight@780: if (p->share_owners[i] == _current_player) truelight@780: p->share_owners[i] = 0xFF; truelight@780: } truelight@780: } celestar@1962: p = GetPlayer(_current_player); truelight@780: /* Sell all the shares that people have on this company */ Darkvater@1797: for (i = 0; i < 4; i++) truelight@780: p->share_owners[i] = 0xFF; truelight@780: } truelight@780: truelight@0: _current_player = old; truelight@0: truelight@0: MarkWholeScreenDirty(); truelight@0: } truelight@0: truelight@0: extern void DeletePlayerWindows(int pi); truelight@0: truelight@0: static void PlayersCheckBankrupt(Player *p) truelight@0: { truelight@0: int owner; truelight@200: int64 val; truelight@0: truelight@543: // If the player has money again, it does not go bankrupt truelight@0: if (p->player_money >= 0) { truelight@0: p->quarters_of_bankrupcy = 0; truelight@0: return; truelight@0: } truelight@0: truelight@0: p->quarters_of_bankrupcy++; truelight@0: truelight@0: owner = p->index; truelight@0: truelight@543: switch (p->quarters_of_bankrupcy) { truelight@543: case 2: truelight@543: AddNewsItem( (StringID)(owner + 16), truelight@543: NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); truelight@543: break; truelight@543: case 3: { truelight@543: /* XXX - In multiplayer, should we ask other players if it wants to take truelight@543: over when it is a human company? -- TrueLight */ truelight@543: if (IS_HUMAN_PLAYER(owner)) { truelight@543: AddNewsItem( (StringID)(owner + 16), truelight@543: NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); truelight@543: break; truelight@543: } truelight@0: truelight@543: // Check if the company has any value.. if not, declare it bankrupt truelight@543: // right now truelight@543: val = CalculateCompanyValue(p); truelight@543: if (val > 0) { truelight@543: p->bankrupt_value = val; truelight@543: p->bankrupt_asked = 1 << owner; // Don't ask the owner truelight@543: p->bankrupt_timeout = 0; truelight@543: break; truelight@543: } truelight@543: // Else, falltrue to case 4... truelight@0: } truelight@543: case 4: { truelight@543: // Close everything the owner has open truelight@543: DeletePlayerWindows(owner); truelight@0: truelight@543: // Show bankrupt news truelight@543: SetDParam(0, p->name_1); truelight@543: SetDParam(1, p->name_2); truelight@543: AddNewsItem( (StringID)(owner + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); truelight@543: truelight@543: // If the player is human, and it is no network play, leave the player playing truelight@543: if (IS_HUMAN_PLAYER(owner) && !_networking) { truelight@543: p->bankrupt_asked = 255; truelight@543: p->bankrupt_timeout = 0x456; truelight@543: } else { truelight@630: #ifdef ENABLE_NETWORK truelight@543: if (IS_HUMAN_PLAYER(owner) && _network_server) { truelight@687: // If we are the server, make sure it is clear that his player is no truelight@687: // longer with us! truelight@543: NetworkClientInfo *ci; truelight@716: NetworkClientState *cs; truelight@687: /* Find all clients that were in control of this company */ truelight@687: FOR_ALL_CLIENTS(cs) { truelight@687: ci = DEREF_CLIENT_INFO(cs); truelight@687: if ((ci->client_playas-1) == owner) { truelight@687: ci->client_playas = OWNER_SPECTATOR; truelight@687: // Send the new info to all the clients truelight@687: NetworkUpdateClientInfo(_network_own_client_index); truelight@687: } truelight@687: } truelight@543: } truelight@543: // Make sure the player no longer controls the company truelight@543: if (IS_HUMAN_PLAYER(owner) && owner == _local_player) { truelight@543: // Switch the player to spectator.. truelight@543: _local_player = OWNER_SPECTATOR; truelight@543: } truelight@687: #endif /* ENABLE_NETWORK */ truelight@687: truelight@543: // Convert everything the player owns to NO_OWNER truelight@543: p->money64 = p->player_money = 100000000; truelight@543: ChangeOwnershipOfPlayerItems(owner, 0xFF); // 255 is no owner truelight@543: // Register the player as not-active truelight@543: p->is_active = false; truelight@543: } truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: void DrawNewsBankrupcy(Window *w) truelight@0: { truelight@0: Player *p; truelight@0: truelight@193: DrawNewsBorder(w); truelight@0: celestar@1962: p = GetPlayer(WP(w,news_d).ni->string_id & 15); truelight@0: DrawPlayerFace(p->face, p->player_color, 2, 23); celestar@2218: GfxFillRect(3, 23, 3+91, 23+118, 0x323 | USE_COLORTABLE); truelight@0: tron@534: SetDParam(0, p->president_name_1); tron@534: SetDParam(1, p->president_name_2); truelight@0: truelight@0: DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94); truelight@0: truelight@0: switch(WP(w,news_d).ni->string_id >> 4) { truelight@0: case 1: truelight@0: DrawStringCentered(w->width>>1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, 0); truelight@0: tron@534: SetDParam(0, p->name_1); tron@534: SetDParam(1, p->name_2); truelight@0: truelight@0: DrawStringMultiCenter( truelight@0: ((w->width - 101) >> 1) + 98, truelight@0: 90, truelight@0: STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED, truelight@0: w->width - 101); truelight@0: break; truelight@0: truelight@0: case 2: { truelight@0: int32 price; truelight@0: truelight@0: DrawStringCentered(w->width>>1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, 0); truelight@0: COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2); tron@534: SetDParam(2, p->name_1); tron@534: SetDParam(3, p->name_2); truelight@0: price = WP(w,news_d).ni->params[2]; tron@534: SetDParam(4, price); truelight@0: DrawStringMultiCenter( truelight@0: ((w->width - 101) >> 1) + 98, truelight@0: 90, truelight@0: price==0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR, truelight@0: w->width - 101); truelight@0: break; truelight@0: } truelight@0: truelight@0: case 3: truelight@0: DrawStringCentered(w->width>>1, 1, STR_705C_BANKRUPT, 0); truelight@0: COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2); truelight@0: DrawStringMultiCenter( truelight@0: ((w->width - 101) >> 1) + 98, truelight@0: 90, truelight@0: STR_705D_HAS_BEEN_CLOSED_DOWN_BY, truelight@0: w->width - 101); truelight@193: break; truelight@0: truelight@0: case 4: truelight@0: DrawStringCentered(w->width>>1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, 0); tron@534: SetDParam(0, p->name_1); tron@534: SetDParam(1, p->name_2); truelight@0: COPY_IN_DPARAM(2,WP(w,news_d).ni->params, 2); truelight@0: DrawStringMultiCenter( truelight@0: ((w->width - 101) >> 1) + 98, truelight@0: 90, truelight@0: STR_705F_STARTS_CONSTRUCTION_NEAR, truelight@0: w->width - 101); truelight@0: break; truelight@0: truelight@0: default: truelight@0: NOT_REACHED(); truelight@0: } truelight@0: } truelight@0: truelight@0: StringID GetNewsStringBankrupcy(NewsItem *ni) truelight@0: { celestar@1962: Player *p = GetPlayer(ni->string_id & 0xF); truelight@0: truelight@0: switch(ni->string_id >> 4) { truelight@0: case 1: tron@534: SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE); tron@534: SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED); tron@534: SetDParam(2, p->name_1); tron@534: SetDParam(3, p->name_2); truelight@0: return STR_02B6; truelight@0: case 2: tron@534: SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER); tron@534: SetDParam(1, STR_705A_HAS_BEEN_SOLD_TO_FOR); truelight@0: COPY_IN_DPARAM(2,ni->params, 2); tron@534: SetDParam(4, p->name_1); tron@534: SetDParam(5, p->name_2); truelight@0: COPY_IN_DPARAM(6,ni->params + 2, 1); truelight@0: return STR_02B6; truelight@0: case 3: tron@534: SetDParam(0, STR_705C_BANKRUPT); tron@534: SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY); truelight@0: COPY_IN_DPARAM(2,ni->params, 2); truelight@0: return STR_02B6; truelight@0: case 4: tron@534: SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED); tron@534: SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR); tron@534: SetDParam(2, p->name_1); tron@534: SetDParam(3, p->name_2); truelight@0: COPY_IN_DPARAM(4,ni->params, 2); truelight@0: return STR_02B6; truelight@0: default: truelight@0: NOT_REACHED(); truelight@0: } truelight@193: truelight@0: /* useless, but avoids compiler warning this way */ truelight@0: return 0; truelight@0: } truelight@0: tron@1093: static void PlayersGenStatistics(void) truelight@0: { truelight@0: Station *st; truelight@0: Player *p; truelight@0: truelight@0: FOR_ALL_STATIONS(st) { truelight@0: if (st->xy != 0) { truelight@0: _current_player = st->owner; truelight@0: SET_EXPENSES_TYPE(EXPENSES_PROPERTY); truelight@0: SubtractMoneyFromPlayer(_price.station_value >> 1); truelight@0: } truelight@0: } truelight@0: truelight@0: if (!HASBIT(1<<0|1<<3|1<<6|1<<9, _cur_month)) truelight@0: return; truelight@0: truelight@0: FOR_ALL_PLAYERS(p) { truelight@0: if (p->is_active) { truelight@0: memmove(&p->old_economy, &p->cur_economy, sizeof(p->old_economy)); truelight@0: memset(&p->cur_economy, 0, sizeof(p->cur_economy)); truelight@0: truelight@0: if (p->num_valid_stat_ent != 24) truelight@0: p->num_valid_stat_ent++; truelight@0: dominik@116: UpdateCompanyRatingAndValue(p, true); truelight@0: PlayersCheckBankrupt(p); truelight@0: truelight@0: if (p->block_preview != 0) truelight@0: p->block_preview--; truelight@0: } truelight@0: } truelight@0: truelight@0: InvalidateWindow(WC_INCOME_GRAPH, 0); truelight@0: InvalidateWindow(WC_OPERATING_PROFIT, 0); truelight@0: InvalidateWindow(WC_DELIVERED_CARGO, 0); truelight@0: InvalidateWindow(WC_PERFORMANCE_HISTORY, 0); truelight@0: InvalidateWindow(WC_COMPANY_VALUE, 0); truelight@0: InvalidateWindow(WC_COMPANY_LEAGUE, 0); truelight@0: } truelight@0: truelight@0: static void AddSingleInflation(int32 *value, uint16 *frac, int32 amt) truelight@0: { truelight@0: int64 tmp; truelight@0: int32 low; truelight@0: tmp = BIGMULS(*value, amt); truelight@0: *frac = (uint16)(low = (uint16)tmp + *frac); truelight@0: *value += (int32)(tmp >> 16) + (low >> 16); truelight@0: } truelight@0: tron@1093: static void AddInflation(void) truelight@0: { truelight@0: int i; truelight@0: int32 inf = _economy.infl_amount * 54; truelight@0: truelight@0: for(i=0; i!=NUM_PRICES; i++) { truelight@0: AddSingleInflation( (int32*)&_price + i, _price_frac + i, inf ); truelight@0: } truelight@0: truelight@0: _economy.max_loan_unround += BIGMULUS(_economy.max_loan_unround, inf, 16); truelight@193: truelight@0: if (_economy.max_loan + 50000 <= _economy.max_loan_unround) truelight@0: _economy.max_loan += 50000; truelight@0: truelight@0: inf = _economy.infl_amount_pr * 54; truelight@0: for(i=0; i!=NUM_CARGO; i++) { truelight@193: AddSingleInflation( hackykid@1884: (int32*)_cargo_payment_rates + i, truelight@0: _cargo_payment_rates_frac + i, truelight@0: inf truelight@0: ); truelight@0: } truelight@0: truelight@0: InvalidateWindowClasses(WC_BUILD_VEHICLE); bjarni@1098: InvalidateWindowClasses(WC_REPLACE_VEHICLE); truelight@0: InvalidateWindowClasses(WC_VEHICLE_DETAILS); truelight@0: InvalidateWindow(WC_PAYMENT_RATES, 0); truelight@0: } truelight@0: tron@1093: static void PlayersPayInterest(void) truelight@0: { truelight@0: Player *p; truelight@0: int interest = _economy.interest_rate * 54; truelight@0: truelight@0: FOR_ALL_PLAYERS(p) { truelight@0: if (!p->is_active) truelight@0: continue; truelight@0: truelight@0: _current_player = p->index; truelight@0: SET_EXPENSES_TYPE(EXPENSES_LOAN_INT); truelight@193: truelight@0: SubtractMoneyFromPlayer(BIGMULUS(p->current_loan, interest, 16)); truelight@0: truelight@0: SET_EXPENSES_TYPE(EXPENSES_OTHER); truelight@0: SubtractMoneyFromPlayer(_price.station_value >> 2); truelight@0: } truelight@0: } truelight@0: tron@1093: static void HandleEconomyFluctuations(void) truelight@0: { truelight@0: if (_opt.diff.economy == 0) truelight@0: return; truelight@0: truelight@0: if (--_economy.fluct == 0) { truelight@0: _economy.fluct = - (int)(Random()&3); truelight@0: AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0); truelight@0: } else if (_economy.fluct == -12) { truelight@0: _economy.fluct = (Random()&255) + 312; truelight@0: AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: static byte _price_category[NUM_PRICES] = { truelight@0: 0, 2, 2, 2, 2, 2, 2, 2, truelight@0: 2, 2, 2, 2, 2, 2, 2, 2, truelight@0: 2, 2, 2, 2, 2, 2, 2, 2, truelight@0: 2, 2, 2, 2, 2, 2, 2, 2, truelight@0: 2, 2, 2, 2, 2, 2, 2, 2, truelight@0: 2, 2, 1, 1, 1, 1, 1, 1, truelight@0: 2, truelight@0: }; truelight@0: truelight@0: static const int32 _price_base[NUM_PRICES] = { dominik@9: 100, // station_value dominik@9: 100, // build_rail dominik@9: 95, // build_road dominik@9: 65, // build_signals dominik@9: 275, // build_bridge dominik@9: 600, // build_train_depot dominik@9: 500, // build_road_depot dominik@9: 700, // build_ship_depot dominik@9: 450, // build_tunnel dominik@9: 200, // train_station_track dominik@9: 180, // train_station_length dominik@9: 600, // build_airport dominik@9: 200, // build_bus_station dominik@9: 200, // build_truck_station dominik@9: 350, // build_dock dominik@9: 400000, // build_railvehicle dominik@9: 2000, // build_railwagon dominik@9: 700000, // aircraft_base dominik@9: 14000, // roadveh_base dominik@9: 65000, // ship_base dominik@9: 20, // build_trees dominik@9: 250, // terraform dominik@9: 20, // clear_1 dominik@9: 40, // purchase_land dominik@9: 200, // clear_2 dominik@9: 500, // clear_3 dominik@9: 20, // remove_trees dominik@9: -70, // remove_rail dominik@9: 10, // remove_signals dominik@9: 50, // clear_bridge dominik@9: 80, // remove_train_depot dominik@9: 80, // remove_road_depot dominik@9: 90, // remove_ship_depot dominik@9: 30, // clear_tunnel dominik@9: 10000, // clear_water dominik@9: 50, // remove_rail_station dominik@9: 30, // remove_airport dominik@9: 50, // remove_bus_station dominik@9: 50, // remove_truck_station dominik@9: 55, // remove_dock dominik@9: 1600, // remove_house dominik@9: 40, // remove_road dominik@9: 5600, // running_rail[0] railroad dominik@9: 5200, // running_rail[1] monorail dominik@9: 4800, // running_rail[2] maglev dominik@9: 9600, // aircraft_running dominik@9: 1600, // roadveh_running dominik@9: 5600, // ship_running dominik@9: 1000000, // build_industry truelight@0: }; truelight@0: tron@1093: void StartupEconomy(void) truelight@0: { truelight@0: int i; truelight@0: truelight@0: assert(sizeof(_price) == NUM_PRICES * sizeof(int32)); truelight@0: truelight@0: for(i=0; i!=NUM_PRICES; i++) { truelight@0: int32 price = _price_base[i]; truelight@0: if (_price_category[i] != 0) { truelight@0: uint mod = _price_category[i] == 1 ? _opt.diff.vehicle_costs : _opt.diff.construction_cost; truelight@0: if (mod < 1) { truelight@0: price = price * 3 >> 2; truelight@0: } else if (mod > 1) { truelight@0: price = price * 9 >> 3; truelight@0: } truelight@0: } truelight@0: ((int32*)&_price)[i] = price; truelight@0: _price_frac[i] = 0; truelight@0: } truelight@0: truelight@0: _economy.interest_rate = _opt.diff.initial_interest; truelight@0: _economy.infl_amount = _opt.diff.initial_interest; truelight@0: _economy.infl_amount_pr = max(0, _opt.diff.initial_interest - 1); truelight@0: _economy.max_loan_unround = _economy.max_loan = _opt.diff.max_loan * 1000; tron@2150: _economy.fluct = GB(Random(), 0, 8) + 168; truelight@0: } truelight@0: truelight@0: Pair SetupSubsidyDecodeParam(Subsidy *s, bool mode) truelight@0: { tron@1977: TileIndex tile; tron@1977: TileIndex tile2; truelight@0: Pair tp; truelight@0: truelight@0: /* if mode is false, convert into plural */ tron@534: SetDParam(0, _cargoc.names_s[s->cargo_type] + (mode?0:32)); truelight@0: truelight@0: if (s->age < 12) { truelight@0: if (!(s->cargo_type == CT_PASSENGERS || s->cargo_type == CT_MAIL)) { ludde@2070: SetDParam(1, STR_INDUSTRY); ludde@2070: SetDParam(2, s->from); ludde@2070: tile = GetIndustry(s->from)->xy; truelight@0: truelight@0: if (s->cargo_type != CT_GOODS && s->cargo_type != CT_FOOD) { ludde@2070: SetDParam(4, STR_INDUSTRY); ludde@2070: SetDParam(5, s->to); ludde@2070: tile2 = GetIndustry(s->to)->xy; truelight@0: } else { ludde@2070: SetDParam(4, STR_TOWN); ludde@2070: SetDParam(5, s->to); ludde@2070: tile2 = GetTown(s->to)->xy; truelight@0: } truelight@0: } else { ludde@2070: SetDParam(1, STR_TOWN); ludde@2070: SetDParam(2, s->from); ludde@2070: tile = GetTown(s->from)->xy; truelight@0: ludde@2070: SetDParam(4, STR_TOWN); ludde@2070: SetDParam(5, s->to); ludde@2070: tile2 = GetTown(s->to)->xy; truelight@0: } truelight@0: } else { ludde@2070: SetDParam(1, s->from); ludde@2070: tile = GetStation(s->from)->xy; truelight@0: ludde@2070: SetDParam(2, s->to); ludde@2070: tile2 = GetStation(s->to)->xy; truelight@0: } truelight@0: truelight@0: tp.a = tile; truelight@0: tp.b = tile2; truelight@0: truelight@0: return tp; truelight@0: } truelight@0: truelight@820: void DeleteSubsidyWithIndustry(uint16 index) truelight@0: { truelight@0: Subsidy *s; truelight@193: truelight@0: for(s=_subsidies; s != endof(_subsidies); s++) { truelight@0: if (s->cargo_type != 0xFF && s->age < 12 && truelight@0: s->cargo_type != CT_PASSENGERS && s->cargo_type != CT_MAIL && truelight@0: (index == s->from || (s->cargo_type!=CT_GOODS && s->cargo_type!=CT_FOOD && index==s->to))) { truelight@0: s->cargo_type = 0xFF; truelight@0: } truelight@193: } truelight@0: } truelight@0: truelight@820: void DeleteSubsidyWithStation(uint16 index) truelight@0: { truelight@0: Subsidy *s; truelight@0: bool dirty = false; truelight@193: truelight@0: for(s=_subsidies; s != endof(_subsidies); s++) { truelight@0: if (s->cargo_type != 0xFF && s->age >= 12 && truelight@0: (s->from == index || s->to == index)) { truelight@0: s->cargo_type = 0xFF; truelight@0: dirty = true; truelight@0: } truelight@193: } truelight@0: truelight@0: if (dirty) truelight@0: InvalidateWindow(WC_SUBSIDIES_LIST, 0); truelight@0: } truelight@0: truelight@0: typedef struct FoundRoute { truelight@0: uint distance; truelight@0: byte cargo; truelight@0: void *from; truelight@0: void *to; truelight@0: } FoundRoute; truelight@0: truelight@0: static void FindSubsidyPassengerRoute(FoundRoute *fr) truelight@0: { truelight@0: Town *from,*to; truelight@0: truelight@0: fr->distance = (uint)-1; truelight@0: truelight@919: fr->from = from = GetTown(RandomRange(_total_towns)); truelight@0: if (from->xy == 0 || from->population < 400) truelight@0: return; truelight@0: truelight@919: fr->to = to = GetTown(RandomRange(_total_towns)); truelight@0: if (from==to || to->xy == 0 || to->population < 400 || to->pct_pass_transported > 42) truelight@0: return; truelight@0: tron@1245: fr->distance = DistanceManhattan(from->xy, to->xy); truelight@0: } truelight@0: truelight@0: static void FindSubsidyCargoRoute(FoundRoute *fr) truelight@0: { truelight@0: Industry *i; truelight@0: int trans, total; truelight@0: byte cargo; truelight@0: truelight@0: fr->distance = (uint)-1; truelight@0: truelight@919: fr->from = i = GetIndustry(RandomRange(_total_industries)); truelight@0: if (i->xy == 0) truelight@0: return; truelight@0: truelight@0: // Randomize cargo type truelight@543: if (Random()&1 && i->produced_cargo[1] != 0xFF) { truelight@0: cargo = i->produced_cargo[1]; truelight@0: trans = i->pct_transported[1]; truelight@0: total = i->total_production[1]; truelight@0: } else { truelight@0: cargo = i->produced_cargo[0]; truelight@0: trans = i->pct_transported[0]; truelight@0: total = i->total_production[0]; truelight@0: } truelight@0: truelight@0: // Quit if no production in this industry truelight@0: // or if the cargo type is passengers truelight@0: // or if the pct transported is already large enough truelight@0: if (total == 0 || trans > 42 || cargo == 0xFF || cargo == CT_PASSENGERS) truelight@0: return; truelight@0: truelight@0: fr->cargo = cargo; truelight@0: truelight@0: if (cargo == CT_GOODS || cargo == CT_FOOD) { truelight@0: // The destination is a town truelight@919: Town *t = GetTown(RandomRange(_total_towns)); truelight@193: truelight@0: // Only want big towns truelight@0: if (t->xy == 0 || t->population < 900) truelight@0: return; tron@1245: fr->distance = DistanceManhattan(i->xy, t->xy); truelight@0: fr->to = t; truelight@0: } else { truelight@0: // The destination is an industry truelight@919: Industry *i2 = GetIndustry(RandomRange(_total_industries)); truelight@193: truelight@0: // The industry must accept the cargo truelight@193: if (i == i2 || i2->xy == 0 || truelight@0: (cargo != i2->accepts_cargo[0] && truelight@0: cargo != i2->accepts_cargo[1] && truelight@0: cargo != i2->accepts_cargo[2])) truelight@0: return; tron@1245: fr->distance = DistanceManhattan(i->xy, i2->xy); truelight@0: fr->to = i2; truelight@0: } truelight@0: } truelight@0: truelight@193: static bool CheckSubsidyDuplicate(Subsidy *s) truelight@0: { truelight@0: Subsidy *ss; truelight@0: truelight@0: for(ss=_subsidies; ss != endof(_subsidies); ss++) { truelight@193: if (s != ss && truelight@193: // ss->age < 12 && truelight@193: ss->from == s->from && truelight@193: ss->to == s->to && truelight@0: ss->cargo_type == s->cargo_type) { truelight@0: s->cargo_type = 0xFF; truelight@0: return true; truelight@0: } truelight@0: } truelight@0: return false; truelight@0: } truelight@0: signde@239: void RemoteSubsidyAdd(Subsidy *s_new) signde@239: { signde@239: Subsidy *s; signde@239: Pair pair; signde@239: signde@239: // search the first free subsidy darkvater@240: for(s=_subsidies; s != endof(_subsidies); s++) signde@239: if (s->cargo_type == 0xFF) signde@239: break; signde@239: signde@239: memcpy(s,s_new,sizeof(Subsidy)); signde@239: signde@239: pair = SetupSubsidyDecodeParam(s, 0); signde@239: AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b); signde@239: signde@239: InvalidateWindow(WC_SUBSIDIES_LIST, 0); signde@239: } signde@239: tron@1093: static void SubsidyMonthlyHandler(void) truelight@0: { truelight@0: Subsidy *s; truelight@0: Pair pair; truelight@0: Station *st; truelight@0: uint n; truelight@0: FoundRoute fr; truelight@0: bool modified = false; truelight@0: truelight@0: for(s=_subsidies; s != endof(_subsidies); s++) { truelight@0: if (s->cargo_type == 0xFF) truelight@0: continue; truelight@0: truelight@0: if (s->age == 12-1) { truelight@0: pair = SetupSubsidyDecodeParam(s, 1); truelight@0: AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b); truelight@0: s->cargo_type = 0xFF; truelight@0: modified = true; truelight@0: } else if (s->age == 2*12-1) { truelight@919: st = GetStation(s->to); truelight@0: if (st->owner == _local_player) { truelight@0: pair = SetupSubsidyDecodeParam(s, 1); truelight@0: AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b); truelight@0: } truelight@0: s->cargo_type = 0xFF; truelight@0: modified = true; truelight@0: } else { truelight@0: s->age++; truelight@0: } truelight@0: } truelight@0: truelight@0: // 25% chance to go on truelight@543: if (CHANCE16(1,4)) { truelight@0: // Find a free slot truelight@0: s = _subsidies; truelight@0: while (s->cargo_type != 0xFF) { truelight@0: if (++s == endof(_subsidies)) truelight@0: goto no_add; truelight@0: } truelight@193: truelight@0: n = 1000; truelight@0: do { truelight@0: FindSubsidyPassengerRoute(&fr); truelight@0: if (fr.distance <= 70) { truelight@0: s->cargo_type = CT_PASSENGERS; truelight@0: s->from = ((Town*)fr.from)->index; truelight@0: s->to = ((Town*)fr.to)->index; truelight@0: goto add_subsidy; truelight@0: } truelight@0: FindSubsidyCargoRoute(&fr); truelight@0: if (fr.distance <= 70) { truelight@0: s->cargo_type = fr.cargo; truelight@919: s->from = ((Industry*)fr.from)->index; truelight@919: s->to = (fr.cargo == CT_GOODS || fr.cargo == CT_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index; truelight@0: add_subsidy: truelight@0: if (!CheckSubsidyDuplicate(s)) { truelight@0: s->age = 0; truelight@0: pair = SetupSubsidyDecodeParam(s, 0); truelight@0: AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b); truelight@0: modified = true; truelight@0: break; truelight@0: } truelight@0: } signde@239: } while (n--); truelight@0: } truelight@0: no_add:; truelight@0: if (modified) truelight@0: InvalidateWindow(WC_SUBSIDIES_LIST, 0); truelight@0: } truelight@0: Darkvater@1881: static const SaveLoad _subsidies_desc[] = { truelight@0: SLE_VAR(Subsidy,cargo_type, SLE_UINT8), truelight@0: SLE_VAR(Subsidy,age, SLE_UINT8), truelight@820: SLE_CONDVAR(Subsidy,from, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), truelight@820: SLE_CONDVAR(Subsidy,from, SLE_UINT16, 5, 255), truelight@820: SLE_CONDVAR(Subsidy,to, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), truelight@820: SLE_CONDVAR(Subsidy,to, SLE_UINT16, 5, 255), truelight@0: SLE_END() truelight@0: }; truelight@0: tron@1093: static void Save_SUBS(void) truelight@0: { truelight@0: int i; truelight@0: Subsidy *s; truelight@0: truelight@0: for(i=0; i!=lengthof(_subsidies); i++) { truelight@0: s = &_subsidies[i]; truelight@0: if (s->cargo_type != 0xFF) { truelight@0: SlSetArrayIndex(i); truelight@0: SlObject(s, _subsidies_desc); truelight@0: } truelight@0: } truelight@0: } truelight@0: tron@1093: static void Load_SUBS(void) truelight@0: { truelight@0: int index; truelight@0: while ((index = SlIterateArray()) != -1) truelight@0: SlObject(&_subsidies[index], _subsidies_desc); truelight@0: } truelight@0: truelight@0: int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, byte cargo_type) truelight@0: { truelight@0: int cargo = cargo_type; truelight@0: byte f; truelight@0: truelight@0: /* zero the distance if it's the bank and very short transport. */ truelight@0: if (_opt.landscape == LT_NORMAL && cargo == CT_VALUABLES && dist < 10) truelight@0: dist = 0; truelight@0: truelight@0: f = 255; truelight@0: if (transit_days > _cargoc.transit_days_1[cargo]) { truelight@0: transit_days -= _cargoc.transit_days_1[cargo]; truelight@0: f -= transit_days; truelight@193: truelight@0: if (transit_days > _cargoc.transit_days_2[cargo]) { truelight@0: transit_days -= _cargoc.transit_days_2[cargo]; truelight@0: truelight@0: if (f < transit_days) truelight@0: f = 0; truelight@0: else truelight@0: f -= transit_days; truelight@0: } truelight@0: } truelight@0: if (f < 31) f = 31; truelight@0: truelight@0: return BIGMULSS(dist * f * num_pieces, _cargo_payment_rates[cargo], 21); truelight@0: } truelight@0: truelight@0: static void DeliverGoodsToIndustry(TileIndex xy, byte cargo_type, int num_pieces) truelight@0: { truelight@193: Industry *ind, *best; truelight@0: int t, u; truelight@0: truelight@0: /* Check if there's an industry close to the station that accepts truelight@0: * the cargo */ truelight@0: best = NULL; darkvater@33: u = _patches.station_spread + 8; truelight@830: FOR_ALL_INDUSTRIES(ind) { truelight@0: if (ind->xy != 0 && (cargo_type == ind->accepts_cargo[0] || cargo_type truelight@0: == ind->accepts_cargo[1] || cargo_type == ind->accepts_cargo[2]) && truelight@0: ind->produced_cargo[0] != 0xFF && truelight@0: ind->produced_cargo[0] != cargo_type && celestar@1634: (t = DistanceManhattan(ind->xy, xy)) < 2 * u) { truelight@0: u = t; truelight@0: best = ind; truelight@0: } truelight@0: } truelight@0: truelight@0: /* Found one? */ truelight@0: if (best != NULL) { truelight@0: best->was_cargo_delivered = true; truelight@0: best->cargo_waiting[0] = min(best->cargo_waiting[0] + num_pieces, 0xFFFF); truelight@0: } truelight@0: } truelight@0: truelight@0: static bool CheckSubsidised(Station *from, Station *to, byte cargo_type) truelight@0: { truelight@0: Subsidy *s; truelight@0: TileIndex xy; truelight@0: Pair pair; truelight@0: Player *p; truelight@0: truelight@0: // check if there is an already existing subsidy that applies to us truelight@0: for(s=_subsidies; s != endof(_subsidies); s++) { truelight@0: if (s->cargo_type == cargo_type && truelight@0: s->age >= 12 && truelight@0: s->from == from->index && truelight@0: s->to == to->index) truelight@0: return true; truelight@0: } truelight@0: truelight@0: /* check if there's a new subsidy that applies.. */ truelight@0: for(s=_subsidies; s != endof(_subsidies); s++) { truelight@0: if (s->cargo_type == cargo_type && s->age < 12) { truelight@193: truelight@0: /* Check distance from source */ truelight@0: if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL) { truelight@919: xy = GetTown(s->from)->xy; truelight@0: } else { truelight@919: xy = (GetIndustry(s->from))->xy; truelight@0: } tron@1245: if (DistanceMax(xy, from->xy) > 9) truelight@0: continue; truelight@193: truelight@0: /* Check distance from dest */ truelight@0: if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL || cargo_type == CT_GOODS || cargo_type == CT_FOOD) { truelight@919: xy = GetTown(s->to)->xy; truelight@0: } else { truelight@919: xy = (GetIndustry(s->to))->xy; truelight@0: } truelight@0: tron@1245: if (DistanceMax(xy, to->xy) > 9) truelight@0: continue; truelight@0: truelight@0: /* Found a subsidy, change the values to indicate that it's in use */ truelight@0: s->age = 12; truelight@0: s->from = from->index; truelight@0: s->to = to->index; truelight@0: truelight@0: /* Add a news item */ truelight@0: pair = SetupSubsidyDecodeParam(s, 0); tron@1309: InjectDParam(2); truelight@0: celestar@1962: p = GetPlayer(_current_player); tron@534: SetDParam(0, p->name_1); tron@534: SetDParam(1, p->name_2); truelight@0: AddNewsItem( truelight@193: STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier, truelight@0: NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), truelight@0: pair.a, pair.b); truelight@0: truelight@0: InvalidateWindow(WC_SUBSIDIES_LIST, 0); truelight@0: return true; truelight@0: } truelight@0: } truelight@0: return false; truelight@0: } truelight@0: truelight@1266: static int32 DeliverGoods(int num_pieces, byte cargo_type, uint16 source, uint16 dest, byte days_in_transit) truelight@0: { truelight@0: bool subsidised; truelight@0: Station *s_from, *s_to; truelight@0: int32 profit; truelight@0: truelight@0: assert(num_pieces > 0); truelight@0: truelight@0: // Update player statistics truelight@0: { celestar@1962: Player *p = GetPlayer(_current_player); truelight@0: p->cur_economy.delivered_cargo += num_pieces; truelight@0: SETBIT(p->cargo_types, cargo_type); truelight@0: } truelight@0: truelight@0: // Get station pointers. truelight@919: s_from = GetStation(source); truelight@919: s_to = GetStation(dest); truelight@0: truelight@0: // Check if a subsidy applies. truelight@0: subsidised = CheckSubsidised(s_from, s_to, cargo_type); truelight@0: truelight@0: // Increase town's counter for some special goods types celestar@1935: if (cargo_type == CT_FOOD) s_to->town->new_act_food += num_pieces; celestar@1935: if (cargo_type == CT_WATER) s_to->town->new_act_water += num_pieces; truelight@0: truelight@0: // Give the goods to the industry. truelight@0: DeliverGoodsToIndustry(s_to->xy, cargo_type, num_pieces); truelight@193: truelight@0: // Determine profit celestar@1935: profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(s_from->xy, s_to->xy), days_in_transit, cargo_type); truelight@0: truelight@0: // Modify profit if a subsidy is in effect truelight@0: if (subsidised) { truelight@0: if (_opt.diff.subsidy_multiplier < 1) { truelight@0: /* 1.5x */ truelight@0: profit += profit >> 1; truelight@0: } else if (_opt.diff.subsidy_multiplier == 1) { truelight@0: /* 2x */ truelight@0: profit *= 2; truelight@0: } else if (_opt.diff.subsidy_multiplier == 2) { truelight@0: /* 3x */ truelight@0: profit *= 3; truelight@0: } else { truelight@0: /* 4x */ truelight@0: profit *= 4; truelight@0: } truelight@0: } truelight@0: truelight@0: // Computers get 25% extra profit if they're intelligent. truelight@0: if (_opt.diff.competitor_intelligence>=1 && !IS_HUMAN_PLAYER(_current_player)) truelight@0: profit += profit >> 2; truelight@0: truelight@0: return profit; truelight@0: } truelight@0: tron@523: /* tron@523: * Returns true if Vehicle v should wait loading because other vehicle is tron@523: * already loading the same cargo type tron@523: * v = vehicle to load, u = GetFirstInChain(v) tron@523: */ tron@523: static bool LoadWait(const Vehicle *v, const Vehicle *u) { tron@523: const Vehicle *w; tron@523: const Vehicle *x; tron@523: bool has_any_cargo = false; tron@523: tron@555: if (!(u->current_order.flags & OF_FULL_LOAD)) return false; tron@523: tron@523: for (w = u; w != NULL; w = w->next) { tron@523: if (w->cargo_count != 0) { tron@523: if (v->cargo_type == w->cargo_type && tron@523: u->last_station_visited == w->cargo_source) tron@523: return false; tron@523: has_any_cargo = true; tron@523: } tron@523: } tron@523: tron@523: FOR_ALL_VEHICLES(x) { bjarni@1067: if ((x->type != VEH_Train || x->subtype == TS_Front_Engine) && // for all locs tron@523: u->last_station_visited == x->last_station_visited && // at the same station tron@523: !(x->vehstatus & VS_STOPPED) && // not stopped tron@555: x->current_order.type == OT_LOADING && // loading tron@523: u != x) { // not itself tron@523: bool other_has_any_cargo = false; tron@523: bool has_space_for_same_type = false; tron@523: bool other_has_same_type = false; tron@523: tron@523: for (w = x; w != NULL; w = w->next) { tron@523: if (w->cargo_count < w->cargo_cap && v->cargo_type == w->cargo_type) tron@523: has_space_for_same_type = true; tron@523: tron@523: if (w->cargo_count != 0) { tron@523: if (v->cargo_type == w->cargo_type && tron@523: u->last_station_visited == w->cargo_source) tron@523: other_has_same_type = true; tron@523: other_has_any_cargo = true; tron@523: } tron@523: } tron@523: tron@523: if (has_space_for_same_type) { tron@523: if (other_has_same_type) return true; tron@523: if (other_has_any_cargo && !has_any_cargo) return true; tron@523: } tron@523: } tron@523: } tron@523: tron@523: return false; tron@523: } tron@523: truelight@0: int LoadUnloadVehicle(Vehicle *v) truelight@0: { truelight@0: int profit = 0; celestar@1935: int v_profit; //virtual profit for feeder systems celestar@1935: int v_profit_total = 0; truelight@0: int unloading_time = 20; truelight@0: Vehicle *u = v; truelight@0: int result = 0; truelight@1266: uint16 last_visited; truelight@0: Station *st; truelight@0: GoodsEntry *ge; truelight@0: int t; truelight@0: uint count, cap; truelight@260: byte old_player; tron@445: bool completely_empty = true; truelight@0: tron@555: assert(v->current_order.type == OT_LOADING); truelight@0: truelight@0: v->cur_speed = 0; truelight@1539: truelight@1561: old_player = _current_player; truelight@1561: _current_player = v->owner; truelight@1561: truelight@919: st = GetStation(last_visited = v->last_station_visited); truelight@0: tron@499: for (; v != NULL; v = v->next) { tron@499: if (v->cargo_cap == 0) continue; truelight@193: truelight@0: ge = &st->goods[v->cargo_type]; truelight@0: truelight@0: /* unload? */ truelight@0: if (v->cargo_count != 0) { celestar@1935: if ( v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000 && !(u->current_order.flags & OF_TRANSFER) ) { truelight@0: // deliver goods to the station truelight@1416: st->time_since_unload = 0; truelight@1416: truelight@0: unloading_time += v->cargo_count; /* TTDBUG: bug in original TTD */ truelight@0: profit += DeliverGoods(v->cargo_count, v->cargo_type, v->cargo_source, last_visited, v->cargo_days); truelight@0: result |= 1; truelight@0: v->cargo_count = 0; celestar@1935: } else if (u->current_order.flags & ( OF_UNLOAD | OF_TRANSFER) ) { truelight@0: /* unload goods and let it wait at the station */ truelight@0: st->time_since_unload = 0; truelight@193: celestar@1935: v_profit = GetTransportedGoodsIncome( celestar@1935: v->cargo_count, celestar@1935: DistanceManhattan(GetStation(v->cargo_source)->xy, GetStation(last_visited)->xy), celestar@1935: v->cargo_days, celestar@1935: v->cargo_type) * 3 / 2; celestar@1935: celestar@1935: v_profit_total += v_profit; celestar@1935: celestar@1935: celestar@1935: unloading_time += v->cargo_count; truelight@0: if ((t=ge->waiting_acceptance & 0xFFF) == 0) { truelight@0: // No goods waiting at station truelight@0: ge->enroute_time = v->cargo_days; truelight@0: ge->enroute_from = v->cargo_source; truelight@0: } else { truelight@0: // Goods already waiting at station. Set counters to the worst value. truelight@0: if (v->cargo_days >= ge->enroute_time) truelight@0: ge->enroute_time = v->cargo_days; truelight@1266: if (last_visited != ge->enroute_from) truelight@0: ge->enroute_from = v->cargo_source; truelight@0: } truelight@0: // Update amount of waiting cargo truelight@0: ge->waiting_acceptance = (ge->waiting_acceptance &~0xFFF) | min(v->cargo_count + t, 0xFFF); celestar@1935: ge->feeder_profit += v_profit; celestar@1935: u->profit_this_year += v_profit; truelight@0: result |= 2; truelight@0: v->cargo_count = 0; truelight@0: } tron@445: tron@445: if (v->cargo_count != 0) tron@445: completely_empty = false; truelight@0: } truelight@193: truelight@0: /* don't pick up goods that we unloaded */ tron@555: if (u->current_order.flags & OF_UNLOAD) continue; truelight@0: truelight@0: /* update stats */ truelight@0: ge->days_since_pickup = 0; truelight@0: t = u->max_speed; truelight@0: if (u->type == VEH_Road) t >>=1; hackykid@1905: if (u->type == VEH_Train) t = u->u.rail.cached_max_speed; truelight@193: truelight@0: // if last speed is 0, we treat that as if no vehicle has ever visited the station. truelight@0: ge->last_speed = t < 255 ? t : 255; truelight@0: ge->last_age = _cur_year - v->build_year; truelight@0: truelight@0: // If there's goods waiting at the station, and the vehicle truelight@0: // has capacity for it, load it on the vehicle. truelight@0: if ((count=ge->waiting_acceptance & 0xFFF) != 0 && truelight@0: (cap = v->cargo_cap - v->cargo_count) != 0) { celestar@1935: int cargoshare; celestar@1935: int feeder_profit_share; celestar@1935: tron@445: if (v->cargo_count == 0) tron@445: TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO); tron@445: tron@523: /* Skip loading this vehicle if another train/vehicle is already handling tron@523: * the same cargo type at this station */ tron@523: if (_patches.improved_load && LoadWait(v,u)) continue; tron@523: tron@445: /* TODO: Regarding this, when we do gradual loading, we tron@445: * should first unload all vehicles and then start tron@445: * loading them. Since this will cause tron@445: * VEHICLE_TRIGGER_EMPTY to be called at the time when tron@445: * the whole vehicle chain is really totally empty, the tron@445: * @completely_empty assignment can then be safely tron@445: * removed; that's how TTDPatch behaves too. --pasky */ tron@445: completely_empty = false; tron@445: truelight@0: if (cap > count) cap = count; celestar@1935: cargoshare = cap * 10000 / ge->waiting_acceptance; celestar@1935: feeder_profit_share = ge->feeder_profit * cargoshare / 10000; truelight@0: v->cargo_count += cap; truelight@0: ge->waiting_acceptance -= cap; celestar@1935: v->profit_this_year -= feeder_profit_share; celestar@1935: ge->feeder_profit -= feeder_profit_share; truelight@0: unloading_time += cap; truelight@0: st->time_since_load = 0; truelight@193: truelight@0: // And record the source of the cargo, and the days in travel. celestar@1935: v->cargo_source = st->index; //changed this for feeder systems truelight@0: v->cargo_days = ge->enroute_time; truelight@0: result |= 2; truelight@0: st->last_vehicle = v->index; truelight@0: } truelight@0: } truelight@0: celestar@1935: truelight@0: v = u; truelight@0: celestar@1935: if (v_profit_total > 0) celestar@1935: ShowFeederIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, v_profit_total); celestar@1935: truelight@0: if (v->type == VEH_Train) { truelight@0: int num = - (int)GetStationPlatforms(st, v->tile) * 2; truelight@0: do num++; while ( (v=v->next) != NULL); truelight@0: if (num > 0) { truelight@0: unloading_time <<=1; truelight@0: unloading_time += num * unloading_time; truelight@0: } truelight@0: v = u; truelight@0: } truelight@0: truelight@0: v->load_unload_time_rem = unloading_time; truelight@0: tron@445: if (completely_empty) { tron@445: TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY); tron@445: } tron@445: truelight@0: if (result != 0) { truelight@0: InvalidateWindow(WC_VEHICLE_DETAILS, v->index); truelight@0: truelight@0: if (result & 2) truelight@0: InvalidateWindow(WC_STATION_VIEW, last_visited); truelight@0: truelight@0: if (profit != 0) { truelight@0: v->profit_this_year += profit; truelight@0: SubtractMoneyFromPlayer(-profit); truelight@0: truelight@0: if (_current_player == _local_player) tron@541: SndPlayVehicleFx(SND_14_CASHTILL, v); truelight@0: truelight@0: ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, -profit); truelight@0: } truelight@0: } truelight@0: truelight@260: _current_player = old_player; truelight@0: return result; truelight@0: } truelight@0: tron@1093: void PlayersMonthlyLoop(void) truelight@0: { truelight@0: PlayersGenStatistics(); Darkvater@1707: if (_patches.inflation && _cur_year < MAX_YEAR_END) truelight@0: AddInflation(); truelight@0: PlayersPayInterest(); signde@206: // Reset the _current_player flag signde@206: _current_player = OWNER_NONE; truelight@0: HandleEconomyFluctuations(); truelight@0: SubsidyMonthlyHandler(); truelight@0: } truelight@0: truelight@0: static void DoAcquireCompany(Player *p) truelight@0: { truelight@0: Player *owner; truelight@0: int i,pi; truelight@200: int64 value; truelight@0: tron@534: SetDParam(0, p->name_1); tron@534: SetDParam(1, p->name_2); tron@534: SetDParam(2, p->bankrupt_value); truelight@0: AddNewsItem( (StringID)(_current_player + 16*2), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0); truelight@0: truelight@0: // original code does this a little bit differently truelight@0: pi = p->index; truelight@0: ChangeOwnershipOfPlayerItems(pi, _current_player); truelight@0: truelight@0: if (p->bankrupt_value == 0) { celestar@1962: owner = GetPlayer(_current_player); truelight@0: owner->current_loan += p->current_loan; truelight@0: } truelight@0: truelight@0: value = CalculateCompanyValue(p) >> 2; truelight@0: for(i=0; i!=4; i++) { truelight@0: if (p->share_owners[i] != 0xFF) { celestar@1962: owner = GetPlayer(p->share_owners[i]); truelight@0: owner->money64 += value; truelight@0: owner->yearly_expenses[0][EXPENSES_OTHER] += value; truelight@0: UpdatePlayerMoney32(owner); truelight@0: } truelight@0: } truelight@0: truelight@0: p->is_active = false; truelight@0: truelight@0: DeletePlayerWindows(pi); darkvater@967: RebuildVehicleLists(); //Updates the open windows to add the newly acquired vehicles to the lists truelight@0: } truelight@0: truelight@599: extern int GetAmountOwnedBy(Player *p, byte owner); truelight@599: Darkvater@1793: /** Acquire shares in an opposing company. Darkvater@1793: * @param x,y unused Darkvater@1793: * @param p1 player to buy the shares from Darkvater@1793: * @param p2 unused Darkvater@1793: */ truelight@0: int32 CmdBuyShareInCompany(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: Player *p; truelight@200: int64 cost; Darkvater@1793: Darkvater@1793: /* Check if buying shares is allowed (protection against modified clients */ Darkvater@1793: if (p1 >= MAX_PLAYERS || !_patches.allow_shares) return CMD_ERROR; truelight@0: truelight@0: SET_EXPENSES_TYPE(EXPENSES_OTHER); celestar@1962: p = GetPlayer(p1); tron@1019: Darkvater@1793: /* Protect new companies from hostile takeovers */ Darkvater@1793: if (_cur_year - p->inaugurated_year < 6) return_cmd_error(STR_7080_PROTECTED); darkvater@930: truelight@599: /* Those lines are here for network-protection (clients can be slow) */ Darkvater@1793: if (GetAmountOwnedBy(p, OWNER_SPECTATOR) == 0) return 0; darkvater@930: Darkvater@1793: /* We can not buy out a real player (temporarily). TODO: well, enable it obviously */ Darkvater@1793: if (GetAmountOwnedBy(p, OWNER_SPECTATOR) == 1 && !p->is_ai) return 0; tron@1019: truelight@0: cost = CalculateCompanyValue(p) >> 2; truelight@0: if (flags & DC_EXEC) { Darkvater@1793: int i; Darkvater@1793: byte *b = p->share_owners; Darkvater@1793: truelight@0: while (*b != 0xFF) b++; /* share owners is guaranteed to contain at least one 0xFF */ truelight@0: *b = _current_player; truelight@0: Darkvater@1793: for (i = 0; p->share_owners[i] == _current_player;) { truelight@0: if (++i == 4) { truelight@0: p->bankrupt_value = 0; truelight@0: DoAcquireCompany(p); truelight@0: break; truelight@0: } truelight@0: } truelight@0: InvalidateWindow(WC_COMPANY, (int)p1); truelight@0: } truelight@0: return cost; truelight@0: } truelight@0: Darkvater@1793: /** Sell shares in an opposing company. Darkvater@1793: * @param x,y unused Darkvater@1793: * @param p1 player to sell the shares from Darkvater@1793: * @param p2 unused Darkvater@1793: */ truelight@0: int32 CmdSellShareInCompany(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: Player *p; truelight@200: int64 cost; Darkvater@1793: Darkvater@1793: /* Check if buying shares is allowed (protection against modified clients */ Darkvater@1793: if (p1 >= MAX_PLAYERS || !_patches.allow_shares) return CMD_ERROR; truelight@0: truelight@0: SET_EXPENSES_TYPE(EXPENSES_OTHER); celestar@1962: p = GetPlayer(p1); truelight@0: truelight@653: /* Those lines are here for network-protection (clients can be slow) */ Darkvater@1793: if (GetAmountOwnedBy(p, _current_player) == 0) return 0; truelight@653: truelight@0: /* adjust it a little to make it less profitable to sell and buy */ truelight@0: cost = CalculateCompanyValue(p) >> 2; truelight@0: cost = -(cost - (cost >> 7)); truelight@0: truelight@0: if (flags & DC_EXEC) { Darkvater@1793: byte *b = p->share_owners; truelight@0: while (*b != _current_player) b++; /* share owners is guaranteed to contain player */ truelight@0: *b = 0xFF; truelight@0: InvalidateWindow(WC_COMPANY, (int)p1); truelight@0: } truelight@0: return cost; truelight@0: } truelight@0: Darkvater@1793: /** Buy up another company. Darkvater@1793: * When a competing company is gone bankrupt you get the chance to purchase Darkvater@1793: * that company. Darkvater@1793: * @todo currently this only works for AI players Darkvater@1793: * @param x,y unused Darkvater@1793: * @param p1 player/company to buy up Darkvater@1793: * @param p2 unused Darkvater@1793: */ truelight@0: int32 CmdBuyCompany(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: Player *p; Darkvater@1793: Darkvater@1793: /* Disable takeovers in multiplayer games */ Darkvater@1793: if (p1 >= MAX_PLAYERS || _networking) return CMD_ERROR; Darkvater@1793: truelight@0: SET_EXPENSES_TYPE(EXPENSES_OTHER); celestar@1962: p = GetPlayer(p1); Darkvater@1793: Darkvater@1793: if (!p->is_ai) return CMD_ERROR; Darkvater@1793: Darkvater@1793: if (flags & DC_EXEC) { truelight@0: DoAcquireCompany(p); truelight@0: } truelight@0: return p->bankrupt_value; truelight@0: } truelight@0: truelight@0: // Prices tron@1093: static void SaveLoad_PRIC(void) truelight@0: { truelight@0: SlArray(&_price, NUM_PRICES, SLE_INT32); truelight@0: SlArray(&_price_frac, NUM_PRICES, SLE_UINT16); truelight@0: } truelight@0: truelight@0: // Cargo payment rates tron@1093: static void SaveLoad_CAPR(void) truelight@0: { truelight@0: SlArray(&_cargo_payment_rates, NUM_CARGO, SLE_INT32); truelight@0: SlArray(&_cargo_payment_rates_frac, NUM_CARGO, SLE_UINT16); truelight@0: } truelight@0: Darkvater@1881: static const SaveLoad _economy_desc[] = { truelight@0: SLE_VAR(Economy,max_loan, SLE_INT32), truelight@0: SLE_VAR(Economy,max_loan_unround, SLE_INT32), truelight@0: SLE_VAR(Economy,fluct, SLE_FILE_I16 | SLE_VAR_I32), truelight@0: SLE_VAR(Economy,interest_rate, SLE_UINT8), truelight@0: SLE_VAR(Economy,infl_amount, SLE_UINT8), truelight@0: SLE_VAR(Economy,infl_amount_pr, SLE_UINT8), truelight@0: SLE_END() truelight@0: }; truelight@0: truelight@0: // Economy variables tron@1093: static void SaveLoad_ECMY(void) truelight@0: { Darkvater@1881: SlObject(&_economy, _economy_desc); truelight@0: } truelight@0: truelight@0: const ChunkHandler _economy_chunk_handlers[] = { truelight@0: { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH}, truelight@0: { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH}, truelight@0: { 'SUBS', Save_SUBS, Load_SUBS, CH_ARRAY}, truelight@0: { 'ECMY', SaveLoad_ECMY, SaveLoad_ECMY, CH_RIFF | CH_LAST}, truelight@0: };