1
0
Fork 0

Add: Client setting gui.start_spectator

Activates a spectator slot in single player and initiate games as a spectator
pull/13673/head
Samu 2019-02-01 14:34:01 +00:00 committed by SamuXarick
parent e70f20a781
commit 402660e447
12 changed files with 86 additions and 50 deletions

View File

@ -72,8 +72,8 @@ static int32_t ClickMoneyCheat(int32_t, int32_t change_direction)
*/ */
static int32_t ClickChangeCompanyCheat(int32_t new_value, int32_t change_direction) static int32_t ClickChangeCompanyCheat(int32_t new_value, int32_t change_direction)
{ {
while ((uint)new_value < Company::GetPoolSize()) { while ((uint)new_value <= COMPANY_SPECTATOR) {
if (Company::IsValidID((CompanyID)new_value)) { if (Company::IsValidID((CompanyID)new_value) || (new_value == COMPANY_SPECTATOR && _settings_client.gui.start_spectator)) {
SetLocalCompany((CompanyID)new_value); SetLocalCompany((CompanyID)new_value);
return _local_company.base(); return _local_company.base();
} }
@ -303,7 +303,7 @@ struct CheatWindow : Window {
case STR_CHEAT_CHANGE_COMPANY: { case STR_CHEAT_CHANGE_COMPANY: {
SetDParam(0, val + 1); SetDParam(0, val + 1);
uint offset = WidgetDimensions::scaled.hsep_indent + GetStringBoundingBox(ce->str).width; uint offset = WidgetDimensions::scaled.hsep_indent + GetStringBoundingBox(ce->str).width;
DrawCompanyIcon(_local_company, rtl ? text_right - offset - WidgetDimensions::scaled.hsep_indent : text_left + offset, y + icon_y_offset); if (_local_company != COMPANY_SPECTATOR) DrawCompanyIcon(_local_company, rtl ? text_right - offset - WidgetDimensions::scaled.hsep_indent : text_left + offset, y + icon_y_offset);
break; break;
} }

View File

@ -925,9 +925,6 @@ CommandCost CmdCompanyCtrl(DoCommandFlags flags, CompanyCtrlAction cca, CompanyI
case CCA_DELETE: { // Delete a company case CCA_DELETE: { // Delete a company
if (reason >= CRR_END) return CMD_ERROR; if (reason >= CRR_END) return CMD_ERROR;
/* We can't delete the last existing company in singleplayer mode. */
if (!_networking && Company::GetNumItems() == 1) return CMD_ERROR;
Company *c = Company::GetIfValid(company_id); Company *c = Company::GetIfValid(company_id);
if (c == nullptr) return CMD_ERROR; if (c == nullptr) return CMD_ERROR;

View File

@ -2246,7 +2246,7 @@ struct CompanyWindow : Window
reinit |= this->GetWidget<NWidgetStacked>(WID_C_SELECT_HOSTILE_TAKEOVER)->SetDisplayedPlane((local || _local_company == COMPANY_SPECTATOR || !c->is_ai || _networking) ? SZSP_NONE : 0); reinit |= this->GetWidget<NWidgetStacked>(WID_C_SELECT_HOSTILE_TAKEOVER)->SetDisplayedPlane((local || _local_company == COMPANY_SPECTATOR || !c->is_ai || _networking) ? SZSP_NONE : 0);
/* Multiplayer buttons. */ /* Multiplayer buttons. */
reinit |= this->GetWidget<NWidgetStacked>(WID_C_SELECT_MULTIPLAYER)->SetDisplayedPlane((!_networking || !NetworkCanJoinCompany(c->index) || _local_company == c->index) ? (int)SZSP_NONE : 0); reinit |= this->GetWidget<NWidgetStacked>(WID_C_SELECT_MULTIPLAYER)->SetDisplayedPlane(((!_networking && (c->is_ai || local || _local_company != COMPANY_SPECTATOR)) || (_networking && (!NetworkCanJoinCompany(c->index) || _local_company == c->index))) ? (int)SZSP_NONE : 0);
this->SetWidgetDisabledState(WID_C_COMPANY_JOIN, c->is_ai); this->SetWidgetDisabledState(WID_C_COMPANY_JOIN, c->is_ai);
@ -2512,6 +2512,11 @@ struct CompanyWindow : Window
break; break;
case WID_C_COMPANY_JOIN: { case WID_C_COMPANY_JOIN: {
if (!_networking) {
if (_local_company == COMPANY_SPECTATOR) SetLocalCompany((CompanyID)this->window_number);
break;
}
this->query_widget = WID_C_COMPANY_JOIN; this->query_widget = WID_C_COMPANY_JOIN;
CompanyID company = this->window_number; CompanyID company = this->window_number;
if (_network_server) { if (_network_server) {

View File

@ -1480,8 +1480,7 @@ DEF_CONSOLE_CMD(ConReloadAI)
return true; return true;
} }
/* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */ if (Company::IsHumanID(company_id)) {
if (Company::IsHumanID(company_id) || company_id == _local_company) {
IConsolePrint(CC_ERROR, "Company is not controlled by an AI."); IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
return true; return true;
} }
@ -1518,8 +1517,7 @@ DEF_CONSOLE_CMD(ConStopAI)
return true; return true;
} }
/* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */ if (Company::IsHumanID(company_id)) {
if (Company::IsHumanID(company_id) || company_id == _local_company) {
IConsolePrint(CC_ERROR, "Company is not controlled by an AI."); IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
return true; return true;
} }

View File

@ -328,16 +328,10 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
/* In all cases, make spectators of clients connected to that company */ /* In all cases, make spectators of clients connected to that company */
if (_networking) NetworkClientsToSpectators(old_owner); if (_networking) NetworkClientsToSpectators(old_owner);
if (old_owner == _local_company) { if (old_owner == _local_company) {
/* Single player cheated to AI company. /* Single player company bankrupts. Move player to Spectator. */
* There are no spectators in singleplayer mode, so we must pick some other company. */
assert(!_networking); assert(!_networking);
Backup<CompanyID> cur_company2(_current_company); Backup<CompanyID> cur_company2(_current_company);
for (const Company *c : Company::Iterate()) { SetLocalCompany(COMPANY_SPECTATOR);
if (c->index != old_owner) {
SetLocalCompany(c->index);
break;
}
}
cur_company2.Restore(); cur_company2.Restore();
assert(old_owner != _local_company); assert(old_owner != _local_company);
} }
@ -609,10 +603,11 @@ static void CompanyCheckBankrupt(Company *c)
default: default:
case 10: { case 10: {
if (!_networking && _local_company == c->index) { if (!_networking && _local_company == c->index) {
/* If we are in singleplayer mode, leave the company playing. Eg. there /* If we are in singleplayer mode and if spectator mode isn't active,
* is no THE-END, otherwise mark the client as spectator to make sure * leave the company playing. Eg. there is no THE-END, otherwise mark
* they are no longer in control of this company. However... when you * the client as spectator to make sure they are no longer in control
* join another company (cheat) the "unowned" company can bankrupt. */ * of this company. However... when you join another company (cheat)
* the "unowned" company can bankrupt. */
c->bankrupt_asked.Set(); c->bankrupt_asked.Set();
break; break;
} }
@ -624,8 +619,8 @@ static void CompanyCheckBankrupt(Company *c)
* updating the local company triggers an assert later on. In the * updating the local company triggers an assert later on. In the
* case of a network game the command will be processed at a time * case of a network game the command will be processed at a time
* that changing the current company is okay. In case of single * that changing the current company is okay. In case of single
* player we are sure (the above check) that we are not the local * player we ensure that the local company remains the same until
* company and thus we won't be moved. */ * the end of StateGameLoop, where it could be changed. */
if (!_networking || _network_server) { if (!_networking || _network_server) {
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_BANKRUPT, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_BANKRUPT, INVALID_CLIENT_ID);
return; return;
@ -645,7 +640,11 @@ static void CompaniesGenStatistics()
{ {
/* Check for bankruptcy each month */ /* Check for bankruptcy each month */
for (Company *c : Company::Iterate()) { for (Company *c : Company::Iterate()) {
Backup<CompanyID> cur_company(_current_company);
Backup<CompanyID> loc_company(_local_company);
CompanyCheckBankrupt(c); CompanyCheckBankrupt(c);
loc_company.Restore();
cur_company.Restore();
} }
Backup<CompanyID> cur_company(_current_company); Backup<CompanyID> cur_company(_current_company);

View File

@ -644,6 +644,9 @@ STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP :{BLACK}Show det
STR_GRAPH_KEY_CAPTION :{WHITE}Key to company graphs STR_GRAPH_KEY_CAPTION :{WHITE}Key to company graphs
STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP :{BLACK}Toggle graph of this company STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP :{BLACK}Toggle graph of this company
# Company list toolbar dropdown
STR_COMPANY_LIST_NEW_COMPANY :New company
# Company league window # Company league window
STR_COMPANY_LEAGUE_TABLE_CAPTION :{WHITE}Company League Table STR_COMPANY_LEAGUE_TABLE_CAPTION :{WHITE}Company League Table
STR_COMPANY_LEAGUE_COMPANY_NAME :{ORANGE}{COMPANY} {BLACK}{COMPANY_NUM} '{STRING}' STR_COMPANY_LEAGUE_COMPANY_NAME :{ORANGE}{COMPANY} {BLACK}{COMPANY_NUM} '{STRING}'

View File

@ -842,8 +842,8 @@ static void OnStartScenario()
static void OnStartGame(bool dedicated_server) static void OnStartGame(bool dedicated_server)
{ {
/* Update the local company for a loaded game. It is either the first available company /* Update the local company for a loaded game. It is either the first available company
* or in the case of a dedicated server, a spectator */ * or, in the case of starting as spectator or a dedicated server, a spectator. */
SetLocalCompany(dedicated_server ? COMPANY_SPECTATOR : GetFirstPlayableCompanyID()); SetLocalCompany((dedicated_server || _settings_client.gui.start_spectator) ? COMPANY_SPECTATOR : GetFirstPlayableCompanyID());
NetworkOnGameStart(); NetworkOnGameStart();
@ -862,23 +862,25 @@ static void MakeNewGameDone()
return; return;
} }
/* Create a single company */ if (!_settings_client.gui.start_spectator) {
DoStartupNewCompany(false); /* Create a single company */
DoStartupNewCompany(false);
Company *c = Company::Get(CompanyID::Begin()); Company *c = Company::Get(CompanyID::Begin());
c->settings = _settings_client.company; c->settings = _settings_client.company;
/* Overwrite color from settings if needed /* Overwrite color from settings if needed
* COLOUR_END corresponds to Random colour */ * COLOUR_END corresponds to Random colour */
if (_settings_client.gui.starting_colour != COLOUR_END) { if (_settings_client.gui.starting_colour != COLOUR_END) {
c->colour = _settings_client.gui.starting_colour; c->colour = _settings_client.gui.starting_colour;
ResetCompanyLivery(c); ResetCompanyLivery(c);
_company_colours[c->index] = c->colour; _company_colours[c->index] = c->colour;
} }
if (_settings_client.gui.starting_colour_secondary != COLOUR_END && HasBit(_loaded_newgrf_features.used_liveries, LS_DEFAULT)) { if (_settings_client.gui.starting_colour_secondary != COLOUR_END && HasBit(_loaded_newgrf_features.used_liveries, LS_DEFAULT)) {
Command<CMD_SET_COMPANY_COLOUR>::Post(LS_DEFAULT, false, _settings_client.gui.starting_colour_secondary); Command<CMD_SET_COMPANY_COLOUR>::Post(LS_DEFAULT, false, _settings_client.gui.starting_colour_secondary);
}
} }
OnStartGame(false); OnStartGame(false);
@ -1231,6 +1233,7 @@ void StateGameLoop()
Layouter::ReduceLineCache(); Layouter::ReduceLineCache();
bool valid_local_company = _game_mode != GM_EDITOR && !_networking && _settings_client.gui.start_spectator && Company::IsValidID(_local_company);
if (_game_mode == GM_EDITOR) { if (_game_mode == GM_EDITOR) {
BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP); BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
RunTileLoop(); RunTileLoop();
@ -1281,6 +1284,10 @@ void StateGameLoop()
} }
assert(IsLocalCompany()); assert(IsLocalCompany());
if (valid_local_company && !Company::IsValidID(_local_company)) {
/* _local_company no longer exists due to bankruptcy. */
SetLocalCompany(COMPANY_SPECTATOR);
}
} }
/** Interval for regular autosaves. Initialized at zero to disable till settings are loaded. */ /** Interval for regular autosaves. Initialized at zero to disable till settings are loaded. */

View File

@ -3377,11 +3377,10 @@ bool AfterLoadGame()
* starting a new company. */ * starting a new company. */
StartScripts(); StartScripts();
/* If Load Scenario / New (Scenario) Game is used, /* If Load Scenario / New (Scenario) Game is used, a company does not exist yet.
* a company does not exist yet. So create one here. * Create one here whenever start_spectator is disabled.
* 1 exception: network-games. Those can have 0 companies * 1 exception: dedicated network servers. Those can have 0 companies. */
* But this exception is not true for non-dedicated network servers! */ if (!_settings_client.gui.start_spectator && !_networking || (_networking && _network_server && !_network_dedicated)) {
if (!_networking || (_networking && _network_server && !_network_dedicated)) {
CompanyID first_human_company = GetFirstPlayableCompanyID(); CompanyID first_human_company = GetFirstPlayableCompanyID();
if (!Company::IsValidID(first_human_company)) { if (!Company::IsValidID(first_human_company)) {
Company *c = DoStartupNewCompany(false, first_human_company); Company *c = DoStartupNewCompany(false, first_human_company);

View File

@ -1139,11 +1139,9 @@ struct ScriptDebugWindow : public Window {
this->SetWidgetDisabledState(WID_SCRD_SETTINGS, this->filter.script_debug_company == CompanyID::Invalid() || this->SetWidgetDisabledState(WID_SCRD_SETTINGS, this->filter.script_debug_company == CompanyID::Invalid() ||
GetConfig(this->filter.script_debug_company)->GetConfigList()->empty()); GetConfig(this->filter.script_debug_company)->GetConfigList()->empty());
extern CompanyID _local_company;
this->SetWidgetDisabledState(WID_SCRD_RELOAD_TOGGLE, this->SetWidgetDisabledState(WID_SCRD_RELOAD_TOGGLE,
this->filter.script_debug_company == CompanyID::Invalid() || this->filter.script_debug_company == CompanyID::Invalid() ||
this->filter.script_debug_company == OWNER_DEITY || this->filter.script_debug_company == OWNER_DEITY);
this->filter.script_debug_company == _local_company);
this->SetWidgetDisabledState(WID_SCRD_CONTINUE_BTN, this->filter.script_debug_company == CompanyID::Invalid() || this->SetWidgetDisabledState(WID_SCRD_CONTINUE_BTN, this->filter.script_debug_company == CompanyID::Invalid() ||
(this->filter.script_debug_company == OWNER_DEITY ? !Game::IsPaused() : !AI::IsPaused(this->filter.script_debug_company))); (this->filter.script_debug_company == OWNER_DEITY ? !Game::IsPaused() : !AI::IsPaused(this->filter.script_debug_company)));
} }

View File

@ -230,6 +230,7 @@ struct GUISettings {
bool show_date_in_logs; ///< whether to show dates in console logs bool show_date_in_logs; ///< whether to show dates in console logs
bool newgrf_developer_tools; ///< activate NewGRF developer tools and allow modifying NewGRFs in an existing game bool newgrf_developer_tools; ///< activate NewGRF developer tools and allow modifying NewGRFs in an existing game
bool ai_developer_tools; ///< activate AI/GS developer tools bool ai_developer_tools; ///< activate AI/GS developer tools
bool start_spectator; ///< activate a spectator slot in single player and initiate games as a spectator
bool scenario_developer; ///< activate scenario developer: allow modifying NewGRFs in an existing game bool scenario_developer; ///< activate scenario developer: allow modifying NewGRFs in an existing game
uint8_t settings_restriction_mode; ///< selected restriction mode in adv. settings GUI. @see RestrictionMode uint8_t settings_restriction_mode; ///< selected restriction mode in adv. settings GUI. @see RestrictionMode
bool newgrf_show_old_versions; ///< whether to show old versions in the NewGRF list bool newgrf_show_old_versions; ///< whether to show old versions in the NewGRF list

View File

@ -816,6 +816,12 @@ def = false
post_cb = [](auto) { InvalidateWindowClassesData(WC_GAME_OPTIONS); InvalidateWindowClassesData(WC_SCRIPT_DEBUG); InvalidateWindowClassesData(WC_SCRIPT_SETTINGS); } post_cb = [](auto) { InvalidateWindowClassesData(WC_GAME_OPTIONS); InvalidateWindowClassesData(WC_SCRIPT_DEBUG); InvalidateWindowClassesData(WC_SCRIPT_SETTINGS); }
cat = SC_EXPERT cat = SC_EXPERT
[SDTC_BOOL]
var = gui.start_spectator
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync
def = false
post_cb = [](auto) { InvalidateWindowClassesData(WC_COMPANY); }
[SDTC_BOOL] [SDTC_BOOL]
var = gui.scenario_developer var = gui.scenario_developer
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync

View File

@ -141,6 +141,7 @@ static void PopupMainToolbarMenu(Window *w, WidgetID widget, const std::initiali
static const int CTMN_CLIENT_LIST = -1; ///< Show the client list static const int CTMN_CLIENT_LIST = -1; ///< Show the client list
static const int CTMN_SPECTATE = -2; ///< Become spectator static const int CTMN_SPECTATE = -2; ///< Become spectator
static const int CTMN_SPECTATOR = -3; ///< Show a company window as spectator static const int CTMN_SPECTATOR = -3; ///< Show a company window as spectator
static const int CTMN_NEW_COMPANY = -4; ///< Create a new company
/** /**
* Pop up a generic company list menu. * Pop up a generic company list menu.
@ -154,8 +155,19 @@ static void PopupMainCompanyToolbMenu(Window *w, WidgetID widget, CompanyMask gr
switch (widget) { switch (widget) {
case WID_TN_COMPANIES: case WID_TN_COMPANIES:
if (!_networking) break; if (!_networking) {
if (_local_company == COMPANY_SPECTATOR) {
bool human = false;
for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
if (Company::IsValidHumanID(c)) {
human = true;
break;
}
}
if (!human) list.push_back(MakeDropDownListStringItem(STR_COMPANY_LIST_NEW_COMPANY, CTMN_NEW_COMPANY, Company::GetNumItems() >= MAX_COMPANIES));
}
break;
}
/* Add the client list button for the companies menu */ /* Add the client list button for the companies menu */
list.push_back(MakeDropDownListStringItem(STR_NETWORK_COMPANY_LIST_CLIENT_LIST, CTMN_CLIENT_LIST)); list.push_back(MakeDropDownListStringItem(STR_NETWORK_COMPANY_LIST_CLIENT_LIST, CTMN_CLIENT_LIST));
@ -594,6 +606,17 @@ static CallBackFunction MenuClickCompany(int index)
return CBF_NONE; return CBF_NONE;
} }
} }
if (!_networking && _local_company == COMPANY_SPECTATOR) {
if (index == CTMN_NEW_COMPANY) {
extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = CompanyID::Invalid());
Company *c = DoStartupNewCompany(false);
c->settings = _settings_client.company;
SetLocalCompany(c->index);
return CBF_NONE;
}
}
ShowCompany((CompanyID)index); ShowCompany((CompanyID)index);
return CBF_NONE; return CBF_NONE;
} }