Changeset - r27525:731028285759
[Not reviewed]
master
0 9 0
Patric Stout - 18 months ago 2023-06-05 17:32:22
truebrain@openttd.org
Feature: allow to do a hostile takeover of an AI company (in singleplayer) (#10914)

With the removal of the share-system, you could no longer make an
AI disappear in a single player game. At least, not without going
into the console.
9 files changed with 136 insertions and 36 deletions:
0 comments (0 inline, 0 general)
src/company_base.h
Show inline comments
 
@@ -165,6 +165,7 @@ struct Company : CompanyProperties, Comp
 
};
 

	
 
Money CalculateCompanyValue(const Company *c, bool including_loan = true);
 
Money CalculateHostileTakeoverValue(const Company *c);
 

	
 
extern uint _cur_company_tick_index;
 

	
src/company_cmd.cpp
Show inline comments
 
@@ -709,7 +709,7 @@ static void HandleBankruptcyTakeover(Com
 

	
 
	AI::NewEvent(best->index, new ScriptEventCompanyAskMerger(c->index, c->bankrupt_value));
 
	if (IsInteractiveCompany(best->index)) {
 
		ShowBuyCompanyDialog(c->index);
 
		ShowBuyCompanyDialog(c->index, false);
 
	}
 
}
 

	
src/company_func.h
Show inline comments
 
@@ -19,7 +19,7 @@ bool MayCompanyTakeOver(CompanyID cbig, 
 
void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner);
 
void GetNameOfOwner(Owner owner, TileIndex tile);
 
void SetLocalCompany(CompanyID new_company);
 
void ShowBuyCompanyDialog(CompanyID company);
 
void ShowBuyCompanyDialog(CompanyID company, bool hostile_takeover);
 
void CompanyAdminUpdate(const Company *company);
 
void CompanyAdminBankrupt(CompanyID company_id);
 
void UpdateLandscapingLimits();
src/company_gui.cpp
Show inline comments
 
@@ -2230,6 +2230,12 @@ static const NWidgetPart _nested_company
 
						NWidget(NWID_SPACER), SetFill(0, 1),
 
						NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
 
							NWidget(NWID_SPACER), SetFill(1, 0),
 
							NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_HOSTILE_TAKEOVER),
 
								NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_HOSTILE_TAKEOVER), SetDataTip(STR_COMPANY_VIEW_HOSTILE_TAKEOVER_BUTTON, STR_COMPANY_VIEW_HOSTILE_TAKEOVER_TOOLTIP),
 
							EndContainer(),
 
						EndContainer(),
 
						NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
 
							NWidget(NWID_SPACER), SetFill(1, 0),
 
							NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_GIVE_MONEY),
 
								NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_GIVE_MONEY), SetDataTip(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON, STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP),
 
							EndContainer(),
 
@@ -2332,6 +2338,13 @@ struct CompanyWindow : Window
 
				wi->SetDisplayedPlane(plane);
 
				reinit = true;
 
			}
 
			/* Enable/disable 'Hostile Takeover' button. */
 
			plane = ((local || _local_company == COMPANY_SPECTATOR || !c->is_ai || _networking) ? SZSP_NONE : 0);
 
			wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_HOSTILE_TAKEOVER);
 
			if (plane != wi->shown_plane) {
 
				wi->SetDisplayedPlane(plane);
 
				reinit = true;
 
			}
 

	
 
			/* Multiplayer buttons. */
 
			plane = ((!_networking) ? (int)SZSP_NONE : (int)(local ? CWP_MP_C_PWD : CWP_MP_C_JOIN));
 
@@ -2398,6 +2411,7 @@ struct CompanyWindow : Window
 
			case WID_C_RELOCATE_HQ:
 
			case WID_C_VIEW_INFRASTRUCTURE:
 
			case WID_C_GIVE_MONEY:
 
			case WID_C_HOSTILE_TAKEOVER:
 
			case WID_C_COMPANY_PASSWORD:
 
			case WID_C_COMPANY_JOIN:
 
				size->width = GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width;
 
@@ -2405,6 +2419,7 @@ struct CompanyWindow : Window
 
				size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width);
 
				size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width);
 
				size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width);
 
				size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_HOSTILE_TAKEOVER_BUTTON).width);
 
				size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width);
 
				size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width);
 
				size->width += padding.width;
 
@@ -2600,6 +2615,10 @@ struct CompanyWindow : Window
 
				ShowQueryString(STR_EMPTY, STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION, 30, this, CS_NUMERAL, QSF_NONE);
 
				break;
 

	
 
			case WID_C_HOSTILE_TAKEOVER:
 
				ShowBuyCompanyDialog((CompanyID)this->window_number, true);
 
				break;
 

	
 
			case WID_C_COMPANY_PASSWORD:
 
				if (this->window_number == _local_company) ShowNetworkCompanyPasswordWindow(this);
 
				break;
 
@@ -2697,9 +2716,12 @@ void DirtyCompanyInfrastructureWindows(C
 
}
 

	
 
struct BuyCompanyWindow : Window {
 
	BuyCompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
 
	BuyCompanyWindow(WindowDesc *desc, WindowNumber window_number, bool hostile_takeover) : Window(desc), hostile_takeover(hostile_takeover)
 
	{
 
		this->InitNested(window_number);
 

	
 
		const Company *c = Company::Get((CompanyID)this->window_number);
 
		this->company_value = hostile_takeover ? CalculateHostileTakeoverValue(c) : c->bankrupt_value;
 
	}
 

	
 
	void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
 
@@ -2712,8 +2734,8 @@ struct BuyCompanyWindow : Window {
 
			case WID_BC_QUESTION:
 
				const Company *c = Company::Get((CompanyID)this->window_number);
 
				SetDParam(0, c->index);
 
				SetDParam(1, c->bankrupt_value);
 
				size->height = GetStringHeight(STR_BUY_COMPANY_MESSAGE, size->width);
 
				SetDParam(1, this->company_value);
 
				size->height = GetStringHeight(this->hostile_takeover ? STR_BUY_COMPANY_HOSTILE_TAKEOVER : STR_BUY_COMPANY_MESSAGE, size->width);
 
				break;
 
		}
 
	}
 
@@ -2740,8 +2762,8 @@ struct BuyCompanyWindow : Window {
 
			case WID_BC_QUESTION: {
 
				const Company *c = Company::Get((CompanyID)this->window_number);
 
				SetDParam(0, c->index);
 
				SetDParam(1, c->bankrupt_value);
 
				DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_BUY_COMPANY_MESSAGE, TC_FROMSTRING, SA_CENTER);
 
				SetDParam(1, this->company_value);
 
				DrawStringMultiLine(r.left, r.right, r.top, r.bottom, this->hostile_takeover ? STR_BUY_COMPANY_HOSTILE_TAKEOVER : STR_BUY_COMPANY_MESSAGE, TC_FROMSTRING, SA_CENTER);
 
				break;
 
			}
 
		}
 
@@ -2755,10 +2777,29 @@ struct BuyCompanyWindow : Window {
 
				break;
 

	
 
			case WID_BC_YES:
 
				Command<CMD_BUY_COMPANY>::Post(STR_ERROR_CAN_T_BUY_COMPANY, (CompanyID)this->window_number);
 
				Command<CMD_BUY_COMPANY>::Post(STR_ERROR_CAN_T_BUY_COMPANY, (CompanyID)this->window_number, this->hostile_takeover);
 
				break;
 
		}
 
	}
 

	
 
	/**
 
	 * Check on a regular interval if the company value has changed.
 
	 */
 
	IntervalTimer<TimerWindow> rescale_interval = {std::chrono::seconds(3), [this](auto) {
 
		/* Value can't change when in bankruptcy. */
 
		if (!this->hostile_takeover) return;
 

	
 
		const Company *c = Company::Get((CompanyID)this->window_number);
 
		auto new_value = CalculateHostileTakeoverValue(c);
 
		if (new_value != this->company_value) {
 
			this->company_value = new_value;
 
			this->ReInit();
 
		}
 
	}};
 

	
 
private:
 
	bool hostile_takeover; ///< Whether the window is showing a hostile takeover.
 
	Money company_value; ///< The value of the company for which the user can buy it.
 
};
 

	
 
static const NWidgetPart _nested_buy_company_widgets[] = {
 
@@ -2790,8 +2831,12 @@ static WindowDesc _buy_company_desc(
 
/**
 
 * Show the query to buy another company.
 
 * @param company The company to buy.
 
 * @param hostile_takeover Whether this is a hostile takeover.
 
 */
 
void ShowBuyCompanyDialog(CompanyID company)
 
void ShowBuyCompanyDialog(CompanyID company, bool hostile_takeover)
 
{
 
	AllocateWindowDescFront<BuyCompanyWindow>(&_buy_company_desc, company);
 
	auto window = BringWindowToFrontById(WC_BUY_COMPANY, company);
 
	if (window == nullptr) {
 
		new BuyCompanyWindow(&_buy_company_desc, company, hostile_takeover);
 
	}
 
}
src/economy.cpp
Show inline comments
 
@@ -105,15 +105,12 @@ Prices _price;
 
static PriceMultipliers _price_base_multiplier;
 

	
 
/**
 
 * Calculate the value of the company. That is the value of all
 
 * assets (vehicles, stations) and money minus the loan,
 
 * except when including_loan is \c false which is useful when
 
 * we want to calculate the value for bankruptcy.
 
 * @param c the company to get the value of.
 
 * @param including_loan include the loan in the company value.
 
 * @return the value of the company.
 
 * Calculate the value of the assets of a company.
 
 *
 
 * @param c The company to calculate the value of.
 
 * @return The value of the assets of the company.
 
 */
 
Money CalculateCompanyValue(const Company *c, bool including_loan)
 
static Money CalculateCompanyAssetValue(const Company *c)
 
{
 
	Owner owner = c->index;
 

	
 
@@ -136,6 +133,22 @@ Money CalculateCompanyValue(const Compan
 
		}
 
	}
 

	
 
	return value;
 
}
 

	
 
/**
 
 * Calculate the value of the company. That is the value of all
 
 * assets (vehicles, stations) and money (including loan),
 
 * except when including_loan is \c false which is useful when
 
 * we want to calculate the value for bankruptcy.
 
 * @param c the company to get the value of.
 
 * @param including_loan include the loan in the company value.
 
 * @return the value of the company.
 
 */
 
Money CalculateCompanyValue(const Company *c, bool including_loan)
 
{
 
	Money value = CalculateCompanyAssetValue(c);
 

	
 
	/* Add real money value */
 
	if (including_loan) value -= c->current_loan;
 
	value += c->money;
 
@@ -144,6 +157,39 @@ Money CalculateCompanyValue(const Compan
 
}
 

	
 
/**
 
 * Calculate what you have to pay to take over a company.
 
 *
 
 * This is different from bankruptcy and company value, and involves a few
 
 * more parameters to make it more realistic.
 
 *
 
 * You have to pay for:
 
 * - The value of all the assets in the company.
 
 * - The loan the company has (the investors really want their money back).
 
 * - The profit for the next two years (if positive) based on the last four quarters.
 
 *
 
 * And on top of that, they walk away with all the money they have in the bank.
 
 *
 
 * @param c the company to get the value of.
 
 * @return The value of the company.
 
 */
 
Money CalculateHostileTakeoverValue(const Company *c)
 
{
 
	Money value = CalculateCompanyAssetValue(c);
 

	
 
	value += c->current_loan;
 
	/* Negative balance is basically a loan. */
 
	if (c->money < 0) {
 
		value += -c->money;
 
	}
 

	
 
	for (int quarter = 0; quarter < 4; quarter++) {
 
		value += std::max<Money>(c->old_economy[quarter].income - c->old_economy[quarter].expenses, 0) * 2;
 
	}
 

	
 
	return std::max<Money>(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)
 
 * @param update the economy with calculated score
 
@@ -1940,14 +1986,14 @@ static IntervalTimer<TimerGameCalendar> 
 
	HandleEconomyFluctuations();
 
});
 

	
 
static void DoAcquireCompany(Company *c)
 
static void DoAcquireCompany(Company *c, bool hostile_takeover)
 
{
 
	CompanyID ci = c->index;
 

	
 
	CompanyNewsInformation *cni = new CompanyNewsInformation(c, Company::Get(_current_company));
 

	
 
	SetDParam(0, STR_NEWS_COMPANY_MERGER_TITLE);
 
	SetDParam(1, c->bankrupt_value == 0 ? STR_NEWS_MERGER_TAKEOVER_TITLE : STR_NEWS_COMPANY_MERGER_DESCRIPTION);
 
	SetDParam(1, hostile_takeover ? STR_NEWS_MERGER_TAKEOVER_TITLE : STR_NEWS_COMPANY_MERGER_DESCRIPTION);
 
	SetDParamStr(2, cni->company_name);
 
	SetDParamStr(3, cni->other_company_name);
 
	SetDParam(4, c->bankrupt_value);
 
@@ -1957,14 +2003,6 @@ static void DoAcquireCompany(Company *c)
 

	
 
	ChangeOwnershipOfCompanyItems(ci, _current_company);
 

	
 
	if (c->bankrupt_value == 0) {
 
		Company *owner = Company::Get(_current_company);
 

	
 
		/* Get both the balance and the loan of the company you just bought. */
 
		SubtractMoneyFromCompany(CommandCost(EXPENSES_OTHER, -c->money));
 
		owner->current_loan += c->current_loan;
 
	}
 

	
 
	if (c->is_ai) AI::Stop(c->index);
 

	
 
	CloseCompanyWindows(ci);
 
@@ -1983,15 +2021,23 @@ static void DoAcquireCompany(Company *c)
 
 * @todo currently this only works for AI companies
 
 * @param flags type of operation
 
 * @param target_company company to buy up
 
 * @param hostile_takeover whether to buy up the company even if it is not bankrupt
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdBuyCompany(DoCommandFlag flags, CompanyID target_company)
 
CommandCost CmdBuyCompany(DoCommandFlag flags, CompanyID target_company, bool hostile_takeover)
 
{
 
	Company *c = Company::GetIfValid(target_company);
 
	if (c == nullptr) return CMD_ERROR;
 

	
 
	/* If you do a hostile takeover but the company went bankrupt, buy it via bankruptcy rules. */
 
	if (hostile_takeover && HasBit(c->bankrupt_asked, _current_company)) hostile_takeover = false;
 

	
 
	/* Disable takeovers when not asked */
 
	if (!HasBit(c->bankrupt_asked, _current_company)) return CMD_ERROR;
 
	if (!hostile_takeover && !HasBit(c->bankrupt_asked, _current_company)) return CMD_ERROR;
 

	
 
	/* Only allow hostile takeover of AI companies and when in single player */
 
	if (hostile_takeover && !c->is_ai) return CMD_ERROR;
 
	if (hostile_takeover && _networking) return CMD_ERROR;
 

	
 
	/* Disable taking over the local company in singleplayer mode */
 
	if (!_networking && _local_company == c->index) return CMD_ERROR;
 
@@ -2002,11 +2048,13 @@ CommandCost CmdBuyCompany(DoCommandFlag 
 
	/* Disable taking over when not allowed. */
 
	if (!MayCompanyTakeOver(_current_company, target_company)) return CMD_ERROR;
 

	
 
	/* Get the cost here as the company is deleted in DoAcquireCompany. */
 
	CommandCost cost(EXPENSES_OTHER, c->bankrupt_value);
 
	/* Get the cost here as the company is deleted in DoAcquireCompany.
 
	 * For bankruptcy this amount is calculated when the offer was made;
 
	 * for hostile takeover you pay the current price. */
 
	CommandCost cost(EXPENSES_OTHER, hostile_takeover ? CalculateHostileTakeoverValue(c) : c->bankrupt_value);
 

	
 
	if (flags & DC_EXEC) {
 
		DoAcquireCompany(c);
 
		DoAcquireCompany(c, hostile_takeover);
 
	}
 
	return cost;
 
}
src/economy_cmd.h
Show inline comments
 
@@ -13,7 +13,7 @@
 
#include "command_type.h"
 
#include "company_type.h"
 

	
 
CommandCost CmdBuyCompany(DoCommandFlag flags, CompanyID target_company);
 
CommandCost CmdBuyCompany(DoCommandFlag flags, CompanyID target_company, bool hostile_takeover);
 

	
 
DEF_CMD_TRAIT(CMD_BUY_COMPANY,           CmdBuyCompany,         0, CMDT_MONEY_MANAGEMENT)
 

	
src/lang/english.txt
Show inline comments
 
@@ -846,7 +846,7 @@ STR_NEWS_COMPANY_BANKRUPT_TITLE         
 
STR_NEWS_COMPANY_BANKRUPT_DESCRIPTION                           :{BIG_FONT}{BLACK}{RAW_STRING} has been closed down by creditors and all assets sold off!
 
STR_NEWS_COMPANY_LAUNCH_TITLE                                   :{BIG_FONT}{BLACK}New transport company launched!
 
STR_NEWS_COMPANY_LAUNCH_DESCRIPTION                             :{BIG_FONT}{BLACK}{RAW_STRING} starts construction near {TOWN}!
 
STR_NEWS_MERGER_TAKEOVER_TITLE                                  :{BIG_FONT}{BLACK}{RAW_STRING} has been taken over by {RAW_STRING}!
 
STR_NEWS_MERGER_TAKEOVER_TITLE                                  :{BIG_FONT}{BLACK}{RAW_STRING} has been taken over by {RAW_STRING} for an undisclosed amount!
 
STR_PRESIDENT_NAME_MANAGER                                      :{BLACK}{PRESIDENT_NAME}{}(Manager)
 

	
 
STR_NEWS_NEW_TOWN                                               :{BLACK}{BIG_FONT}{RAW_STRING} sponsored construction of new town {TOWN}!
 
@@ -3760,6 +3760,8 @@ STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON  
 
STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP                         :{BLACK}View detailed infrastructure counts
 
STR_COMPANY_VIEW_GIVE_MONEY_BUTTON                              :{BLACK}Give money
 
STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP                             :{BLACK}Give money to this company
 
STR_COMPANY_VIEW_HOSTILE_TAKEOVER_BUTTON                        :{BLACK}Hostile takeover
 
STR_COMPANY_VIEW_HOSTILE_TAKEOVER_TOOLTIP                       :{BLACK}Do a hostile takeover of this company
 

	
 
STR_COMPANY_VIEW_NEW_FACE_BUTTON                                :{BLACK}New Face
 
STR_COMPANY_VIEW_NEW_FACE_TOOLTIP                               :{BLACK}Select new face for manager
 
@@ -3775,6 +3777,7 @@ STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_
 
STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION                       :Enter the amount of money you want to give
 

	
 
STR_BUY_COMPANY_MESSAGE                                         :{WHITE}We are looking for a transport company to take-over our company.{}{}Do you want to purchase {COMPANY} for {CURRENCY_LONG}?
 
STR_BUY_COMPANY_HOSTILE_TAKEOVER                                :{WHITE}In a hostile takeover of {COMPANY} you will purchase all assets, pay off all loans, and pay two years worth of profits.{}{}The total is estimated to be {CURRENCY_LONG}.{}{}Do you want to continue this hostile takeover?
 

	
 
# Company infrastructure window
 
STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION                         :{WHITE}Infrastructure of {COMPANY}
src/script/api/script_event_types.cpp
Show inline comments
 
@@ -119,7 +119,7 @@ bool ScriptEventEnginePreview::AcceptPre
 
bool ScriptEventCompanyAskMerger::AcceptMerger()
 
{
 
	EnforceCompanyModeValid(false);
 
	return ScriptObject::Command<CMD_BUY_COMPANY>::Do((::CompanyID)this->owner);
 
	return ScriptObject::Command<CMD_BUY_COMPANY>::Do((::CompanyID)this->owner, false);
 
}
 

	
 
ScriptEventAdminPort::ScriptEventAdminPort(const std::string &json) :
src/widgets/company_widget.h
Show inline comments
 
@@ -44,6 +44,9 @@ enum CompanyWidgets {
 
	WID_C_SELECT_GIVE_MONEY,          ///< Selection widget for the give money button.
 
	WID_C_GIVE_MONEY,                 ///< Button to give money.
 

	
 
	WID_C_SELECT_HOSTILE_TAKEOVER,    ///< Selection widget for the hostile takeover button.
 
	WID_C_HOSTILE_TAKEOVER,           ///< Button to hostile takeover another company.
 

	
 
	WID_C_HAS_PASSWORD,               ///< Has company password lock.
 
	WID_C_SELECT_MULTIPLAYER,         ///< Multiplayer selection panel.
 
	WID_C_COMPANY_PASSWORD,           ///< Button to set company password.
0 comments (0 inline, 0 general)