From 62e990823d9fe0349d68a4b038860ecbeafdec69 Mon Sep 17 00:00:00 2001 From: rubidium Date: Tue, 1 Sep 2009 12:57:04 +0000 Subject: [PATCH] (svn r17345) -Fix [FS#2769]: one wasn't offered to take over bankrupt companies anymore; caused by the introduction NoAI, although NewAI had the same problem too. --- src/ai/ai_instance.cpp | 1 + src/ai/api/ai_changelog.hpp | 1 + src/ai/api/ai_event.hpp | 1 + src/ai/api/ai_event.hpp.sq | 1 + src/ai/api/ai_event_types.cpp | 5 +++ src/ai/api/ai_event_types.hpp | 48 ++++++++++++++++++++++ src/ai/api/ai_event_types.hpp.sq | 22 ++++++++++ src/company_cmd.cpp | 69 +++++++++++++++++++++++++++++++- src/company_func.h | 1 + src/economy.cpp | 20 +++------ 10 files changed, 153 insertions(+), 16 deletions(-) diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp index 2d5a012513..6d29aec950 100644 --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -186,6 +186,7 @@ void AIInstance::RegisterAPI() SQAIEngineList_Register(this->engine); SQAIError_Register(this->engine); SQAIEvent_Register(this->engine); + SQAIEventCompanyAskMerger_Register(this->engine); SQAIEventCompanyBankrupt_Register(this->engine); SQAIEventCompanyInTrouble_Register(this->engine); SQAIEventCompanyMerger_Register(this->engine); diff --git a/src/ai/api/ai_changelog.hpp b/src/ai/api/ai_changelog.hpp index c1c9e44be9..6c14b7a2ed 100644 --- a/src/ai/api/ai_changelog.hpp +++ b/src/ai/api/ai_changelog.hpp @@ -19,6 +19,7 @@ * API additions: * \li AIBaseStation * \li AIBuoyList + * \li AIEventCompanyAskMerger * \li AIRail::RemoveRailWaypointTileRect * \li AISubsidy::SubsidyParticipantType * \li AISubsidy::GetSourceType diff --git a/src/ai/api/ai_event.hpp b/src/ai/api/ai_event.hpp index 25330a4cf3..dd12dfbcaa 100644 --- a/src/ai/api/ai_event.hpp +++ b/src/ai/api/ai_event.hpp @@ -36,6 +36,7 @@ public: AI_ET_ENGINE_PREVIEW, AI_ET_COMPANY_NEW, AI_ET_COMPANY_IN_TROUBLE, + AI_ET_COMPANY_ASK_MERGER, AI_ET_COMPANY_MERGER, AI_ET_COMPANY_BANKRUPT, AI_ET_VEHICLE_CRASHED, diff --git a/src/ai/api/ai_event.hpp.sq b/src/ai/api/ai_event.hpp.sq index dd365c4bc3..cc250961f3 100644 --- a/src/ai/api/ai_event.hpp.sq +++ b/src/ai/api/ai_event.hpp.sq @@ -38,6 +38,7 @@ void SQAIEvent_Register(Squirrel *engine) { SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_ENGINE_PREVIEW, "AI_ET_ENGINE_PREVIEW"); SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_NEW, "AI_ET_COMPANY_NEW"); SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_IN_TROUBLE, "AI_ET_COMPANY_IN_TROUBLE"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_ASK_MERGER, "AI_ET_COMPANY_ASK_MERGER"); SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_MERGER, "AI_ET_COMPANY_MERGER"); SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_BANKRUPT, "AI_ET_COMPANY_BANKRUPT"); SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_CRASHED, "AI_ET_VEHICLE_CRASHED"); diff --git a/src/ai/api/ai_event_types.cpp b/src/ai/api/ai_event_types.cpp index 554112f875..5500d15cbd 100644 --- a/src/ai/api/ai_event_types.cpp +++ b/src/ai/api/ai_event_types.cpp @@ -92,3 +92,8 @@ bool AIEventEnginePreview::AcceptPreview() { return AIObject::DoCommand(0, this->engine, 0, CMD_WANT_ENGINE_PREVIEW); } + +bool AIEventCompanyAskMerger::AcceptMerger() +{ + return AIObject::DoCommand(0, this->owner, 0, CMD_BUY_COMPANY); +} diff --git a/src/ai/api/ai_event_types.hpp b/src/ai/api/ai_event_types.hpp index 7c51129498..774df7c441 100644 --- a/src/ai/api/ai_event_types.hpp +++ b/src/ai/api/ai_event_types.hpp @@ -358,6 +358,54 @@ private: AICompany::CompanyID owner; }; +/** + * Event Company Ask Merger, indicating a company can be bought (cheaply) by you. + */ +class AIEventCompanyAskMerger : public AIEvent { +public: + static const char *GetClassName() { return "AIEventCompanyAskMerger"; } + + /** + * @param owner The company that can be bough. + * @param value The value/costs of buying the company. + */ + AIEventCompanyAskMerger(Owner owner, int32 value) : + AIEvent(AI_ET_COMPANY_MERGER), + owner((AICompany::CompanyID)owner), + value(value) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventCompanyAskMerger *Convert(AIEvent *instance) { return (AIEventCompanyAskMerger *)instance; } + + /** + * Get the CompanyID of the company that can be bought. + * @return The CompanyID of the company that can be bought. + * @note If the company is bought this will become invalid. + */ + AICompany::CompanyID GetCompanyID() { return this->owner; } + + /** + * Get the value of the new company. + * @return The value of the new company. + */ + int32 GetValue() { return this->value; } + + /** + * Take over the company for this merger. + * @return true if the merger was a success. + */ + bool AcceptMerger(); + +private: + AICompany::CompanyID owner; + int32 value; +}; + /** * Event Company Merger, indicating a company has been bought by another * company. diff --git a/src/ai/api/ai_event_types.hpp.sq b/src/ai/api/ai_event_types.hpp.sq index f10bb96216..a0842231a2 100644 --- a/src/ai/api/ai_event_types.hpp.sq +++ b/src/ai/api/ai_event_types.hpp.sq @@ -191,6 +191,28 @@ void SQAIEventCompanyInTrouble_Register(Squirrel *engine) { SQAIEventCompanyInTrouble.PostRegister(engine); } +namespace SQConvert { + /* Allow AIEventCompanyAskMerger to be used as Squirrel parameter */ + template <> AIEventCompanyAskMerger *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyAskMerger *)instance; } + template <> AIEventCompanyAskMerger &GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyAskMerger *)instance; } + template <> const AIEventCompanyAskMerger *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyAskMerger *)instance; } + template <> const AIEventCompanyAskMerger &GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyAskMerger *)instance; } + template <> int Return(HSQUIRRELVM vm, AIEventCompanyAskMerger *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyAskMerger", res, NULL, DefSQDestructorCallback); return 1; } +}; // namespace SQConvert + +void SQAIEventCompanyAskMerger_Register(Squirrel *engine) { + DefSQClass SQAIEventCompanyAskMerger("AIEventCompanyAskMerger"); + SQAIEventCompanyAskMerger.PreRegister(engine, "AIEvent"); + + SQAIEventCompanyAskMerger.DefSQStaticMethod(engine, &AIEventCompanyAskMerger::Convert, "Convert", 2, ".x"); + + SQAIEventCompanyAskMerger.DefSQMethod(engine, &AIEventCompanyAskMerger::GetCompanyID, "GetCompanyID", 1, "x"); + SQAIEventCompanyAskMerger.DefSQMethod(engine, &AIEventCompanyAskMerger::GetValue, "GetValue", 1, "x"); + SQAIEventCompanyAskMerger.DefSQMethod(engine, &AIEventCompanyAskMerger::AcceptMerger, "AcceptMerger", 1, "x"); + + SQAIEventCompanyAskMerger.PostRegister(engine); +} + namespace SQConvert { /* Allow AIEventCompanyMerger to be used as Squirrel parameter */ template <> AIEventCompanyMerger *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyMerger *)instance; } diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index f1ae42c700..f47072562c 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -492,13 +492,78 @@ void InitializeCompanies() _cur_company_tick_index = 0; } +/** + * Handle the bankruptcy take over of a company. + * Companies going bankrupt will ask the other companies in order of their + * performance rating, so better performing companies get the 'do you want to + * merge with Y' question earlier. The question will then stay till either the + * company has gone bankrupt or got merged with a company. + * + * @param c the company that is going bankrupt. + */ +static void HandleBankruptcyTakeover(Company *c) +{ + /* Amount of time out for each company to take over a company; + * Timeout is a quarter (3 months of 30 days) divided over the + * number of companies. The minimum number of days in a quarter + * is 90: 31 in January, 28 in February and 31 in March. + * Note that the company going bankrupt can't buy itself. */ + static const int TAKE_OVER_TIMEOUT = 3 * 30 * DAY_TICKS / (MAX_COMPANIES - 1); + + assert(c->bankrupt_asked != 0); + + /* We're currently asking some company to buy 'us' */ + if (c->bankrupt_timeout != 0) { + c->bankrupt_timeout -= MAX_COMPANIES; + if (c->bankrupt_timeout > 0) return; + c->bankrupt_timeout = 0; + + return; + } + + /* Did we ask everyone for bankruptcy? If so, bail out. */ + if (c->bankrupt_asked == MAX_UVALUE(CompanyMask)) return; + + Company *c2, *best = NULL; + int32 best_performance = -1; + + /* Ask the company with the highest performance history first */ + FOR_ALL_COMPANIES(c2) { + if (c2->bankrupt_asked == 0 && // Don't ask companies going bankrupt themselves + !HasBit(c->bankrupt_asked, c2->index) && + best_performance < c2->old_economy[1].performance_history) { + best_performance = c2->old_economy[1].performance_history; + best = c2; + } + } + + /* Asked all companies? */ + if (best_performance == -1) { + c->bankrupt_asked = MAX_UVALUE(CompanyMask); + return; + } + + SetBit(c->bankrupt_asked, best->index); + + if (IsInteractiveCompany(best->index)) { + c->bankrupt_timeout = TAKE_OVER_TIMEOUT; + ShowBuyCompanyDialog(c->index); + return; + } + + if (best->is_ai) { + AI::NewEvent(best->index, new AIEventCompanyAskMerger(c->index, ClampToI32(c->bankrupt_value))); + } +} + void OnTick_Companies() { if (_game_mode == GM_EDITOR) return; Company *c = Company::GetIfValid(_cur_company_tick_index); - if (c != NULL && c->name_1 != 0) { - GenerateCompanyName(c); + if (c != NULL) { + if (c->name_1 != 0) GenerateCompanyName(c); + if (c->bankrupt_asked != 0) HandleBankruptcyTakeover(c); } if (_next_competitor_start == 0) { diff --git a/src/company_func.h b/src/company_func.h index 2f398747ad..c458048ec4 100644 --- a/src/company_func.h +++ b/src/company_func.h @@ -21,6 +21,7 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner); void GetNameOfOwner(Owner owner, TileIndex tile); void SetLocalCompany(CompanyID new_company); +void ShowBuyCompanyDialog(CompanyID company); extern CompanyByte _local_company; extern CompanyByte _current_company; diff --git a/src/economy.cpp b/src/economy.cpp index 997c882d85..335889d83f 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -506,16 +506,6 @@ static void CompanyCheckBankrupt(Company *c) AI::BroadcastNewEvent(new AIEventCompanyInTrouble(c->index)); break; case 3: { - /* XXX - In multiplayer, should we ask other companies if it wants to take - over when it is a human company? -- TrueLight */ - if (!c->is_ai) { - SetDParam(0, STR_NEWS_COMPANY_IN_TROUBLE_TITLE); - SetDParam(1, STR_NEWS_COMPANY_IN_TROUBLE_DESCRIPTION); - SetDParamStr(2, cni->company_name); - AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, NS_COMPANY_TROUBLE, cni); - break; - } - /* Check if the company has any value.. if not, declare it bankrupt * right now */ Money val = CalculateCompanyValue(c); @@ -1560,15 +1550,17 @@ CommandCost CmdSellShareInCompany(TileIndex tile, DoCommandFlag flags, uint32 p1 CommandCost CmdBuyCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Company *c = Company::GetIfValid(p1); + if (c == NULL) return CMD_ERROR; - /* Disable takeovers in multiplayer games */ - if (c == NULL || _networking) return CMD_ERROR; + /* Disable takeovers when not asked */ + if (!HasBit(c->bankrupt_asked, _current_company)) return CMD_ERROR; + + /* Disable taking over the local company in single player */ + if (!_networking && _local_company == c->index) return CMD_ERROR; /* Do not allow companies to take over themselves */ if ((CompanyID)p1 == _current_company) return CMD_ERROR; - if (!c->is_ai) return CMD_ERROR; - if (flags & DC_EXEC) { DoAcquireCompany(c); }