|
@@ -26,49 +26,49 @@
|
|
|
#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];
|
|
|
int _score_part[MAX_PLAYERS][SCORE_END];
|
|
|
|
|
|
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) {
|
|
@@ -188,53 +188,52 @@ int UpdateCompanyRatingAndValue(Player *
|
|
|
/* 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++) {
|
|
|
for (ScoreID i = SCORE_BEGIN; i < SCORE_END; 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);
|
|
@@ -446,49 +445,49 @@ static void PlayersCheckBankrupt(Player
|
|
|
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));
|
|
|
p = GetPlayer((PlayerID)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;
|
|
@@ -515,49 +514,49 @@ void DrawNewsBankrupcy(Window *w)
|
|
|
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));
|
|
|
const Player *p = GetPlayer((PlayerID)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);
|
|
@@ -1541,198 +1540,198 @@ int LoadUnloadVehicle(Vehicle *v, bool j
|
|
|
|
|
|
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;
|
|
|
int i;
|
|
|
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;
|
|
|
PlayerID 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);
|
|
|
extern int GetAmountOwnedBy(const Player *p, PlayerID 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);
|
|
|
p = GetPlayer((PlayerID)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;
|
|
|
PlayerByte* 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);
|
|
|
p = GetPlayer((PlayerID)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;
|
|
|
PlayerByte* 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);
|
|
|
p = GetPlayer((PlayerID)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[] = {
|
|
|
extern 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},
|
|
|
};
|