Changeset - r26103:316b73a1be08
[Not reviewed]
master
0 47 0
Michael Lutz - 3 years ago 2021-10-31 18:39:09
michi@icosahedron.de
Codechange: Template DoCommandP to automagically reflect the parameters of the command proc.

When finished, this will allow each command handler to take individually
different parameters, obliviating the need for bit-packing.
38 files changed:
0 comments (0 inline, 0 general)
src/ai/ai_gui.cpp
Show inline comments
 
@@ -7,48 +7,50 @@
 

	
 
/** @file ai_gui.cpp %Window for configuring the AIs */
 

	
 
#include "../stdafx.h"
 
#include "../table/sprites.h"
 
#include "../error.h"
 
#include "../settings_gui.h"
 
#include "../querystring_gui.h"
 
#include "../stringfilter_type.h"
 
#include "../company_base.h"
 
#include "../company_gui.h"
 
#include "../strings_func.h"
 
#include "../window_func.h"
 
#include "../gfx_func.h"
 
#include "../command_func.h"
 
#include "../network/network.h"
 
#include "../settings_func.h"
 
#include "../network/network_content.h"
 
#include "../textfile_gui.h"
 
#include "../widgets/dropdown_type.h"
 
#include "../widgets/dropdown_func.h"
 
#include "../hotkeys.h"
 
#include "../core/geometry_func.hpp"
 
#include "../guitimer_func.h"
 
#include "../company_cmd.h"
 
#include "../misc_cmd.h"
 

	
 
#include "ai.hpp"
 
#include "ai_gui.hpp"
 
#include "../script/api/script_log.hpp"
 
#include "ai_config.hpp"
 
#include "ai_info.hpp"
 
#include "ai_instance.hpp"
 
#include "../game/game.hpp"
 
#include "../game/game_config.hpp"
 
#include "../game/game_info.hpp"
 
#include "../game/game_instance.hpp"
 

	
 
#include "table/strings.h"
 

	
 
#include <vector>
 

	
 
#include "../safeguards.h"
 

	
 
static ScriptConfig *GetConfig(CompanyID slot)
 
{
 
	if (slot == OWNER_DEITY) return GameConfig::GetConfig();
 
	return AIConfig::GetConfig(slot);
 
}
 

	
 
@@ -1269,138 +1271,138 @@ struct AIDebugWindow : public Window {
 
		this->InvalidateData(-1);
 

	
 
		this->autoscroll = true;
 
		this->last_vscroll_pos = this->vscroll->GetPosition();
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		/* Also called for hotkeys, so check for disabledness */
 
		if (this->IsWidgetDisabled(widget)) return;
 

	
 
		/* Check which button is clicked */
 
		if (IsInsideMM(widget, WID_AID_COMPANY_BUTTON_START, WID_AID_COMPANY_BUTTON_END + 1)) {
 
			ChangeToAI((CompanyID)(widget - WID_AID_COMPANY_BUTTON_START));
 
		}
 

	
 
		switch (widget) {
 
			case WID_AID_SCRIPT_GAME:
 
				ChangeToAI(OWNER_DEITY);
 
				break;
 

	
 
			case WID_AID_RELOAD_TOGGLE:
 
				if (ai_debug_company == OWNER_DEITY) break;
 
				/* First kill the company of the AI, then start a new one. This should start the current AI again */
 
				DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | ai_debug_company << 16 | CRR_MANUAL << 24, 0);
 
				DoCommandP(CMD_COMPANY_CTRL, 0, CCA_NEW_AI | ai_debug_company << 16, 0);
 
				Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | ai_debug_company << 16 | CRR_MANUAL << 24, 0, {});
 
				Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | ai_debug_company << 16, 0, {});
 
				break;
 

	
 
			case WID_AID_SETTINGS:
 
				ShowAISettingsWindow(ai_debug_company);
 
				break;
 

	
 
			case WID_AID_BREAK_STR_ON_OFF_BTN:
 
				this->break_check_enabled = !this->break_check_enabled;
 
				this->InvalidateData(-1);
 
				break;
 

	
 
			case WID_AID_MATCH_CASE_BTN:
 
				this->case_sensitive_break_check = !this->case_sensitive_break_check;
 
				this->InvalidateData(-1);
 
				break;
 

	
 
			case WID_AID_CONTINUE_BTN:
 
				/* Unpause current AI / game script and mark the corresponding script button dirty. */
 
				if (!this->IsDead()) {
 
					if (ai_debug_company == OWNER_DEITY) {
 
						Game::Unpause();
 
					} else {
 
						AI::Unpause(ai_debug_company);
 
					}
 
				}
 

	
 
				/* If the last AI/Game Script is unpaused, unpause the game too. */
 
				if ((_pause_mode & PM_PAUSED_NORMAL) == PM_PAUSED_NORMAL) {
 
					bool all_unpaused = !Game::IsPaused();
 
					if (all_unpaused) {
 
						for (const Company *c : Company::Iterate()) {
 
							if (c->is_ai && AI::IsPaused(c->index)) {
 
								all_unpaused = false;
 
								break;
 
							}
 
						}
 
						if (all_unpaused) {
 
							/* All scripts have been unpaused => unpause the game. */
 
							DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 0);
 
							Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 0, {});
 
						}
 
					}
 
				}
 

	
 
				this->highlight_row = -1;
 
				this->InvalidateData(-1);
 
				break;
 
		}
 
	}
 

	
 
	void OnEditboxChanged(int wid) override
 
	{
 
		if (wid == WID_AID_BREAK_STR_EDIT_BOX) {
 
			/* Save the current string to static member so it can be restored next time the window is opened. */
 
			strecpy(this->break_string, this->break_editbox.text.buf, lastof(this->break_string));
 
			break_string_filter.SetFilterTerm(this->break_string);
 
		}
 
	}
 

	
 
	/**
 
	 * Some data on this window has become invalid.
 
	 * @param data Information about the changed data.
 
	 *             This is the company ID of the AI/GS which wrote a new log message, or -1 in other cases.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	void OnInvalidateData(int data = 0, bool gui_scope = true) override
 
	{
 
		/* If the log message is related to the active company tab, check the break string.
 
		 * This needs to be done in gameloop-scope, so the AI is suspended immediately. */
 
		if (!gui_scope && data == ai_debug_company && this->IsValidDebugCompany(ai_debug_company) && this->break_check_enabled && !this->break_string_filter.IsEmpty()) {
 
			/* Get the log instance of the active company */
 
			ScriptLog::LogData *log = this->GetLogPointer();
 

	
 
			if (log != nullptr) {
 
				this->break_string_filter.ResetState();
 
				this->break_string_filter.AddLine(log->lines[log->pos]);
 
				if (this->break_string_filter.GetState()) {
 
					/* Pause execution of script. */
 
					if (!this->IsDead()) {
 
						if (ai_debug_company == OWNER_DEITY) {
 
							Game::Pause();
 
						} else {
 
							AI::Pause(ai_debug_company);
 
						}
 
					}
 

	
 
					/* Pause the game. */
 
					if ((_pause_mode & PM_PAUSED_NORMAL) == PM_UNPAUSED) {
 
						DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 1);
 
						Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 1, {});
 
					}
 

	
 
					/* Highlight row that matched */
 
					this->highlight_row = log->pos;
 
				}
 
			}
 
		}
 

	
 
		if (!gui_scope) return;
 

	
 
		this->SelectValidDebugCompany();
 

	
 
		ScriptLog::LogData *log = ai_debug_company != INVALID_COMPANY ? this->GetLogPointer() : nullptr;
 
		this->vscroll->SetCount((log == nullptr) ? 0 : log->used);
 

	
 
		/* Update company buttons */
 
		for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
 
			this->SetWidgetDisabledState(i + WID_AID_COMPANY_BUTTON_START, !Company::IsValidAiID(i));
 
			this->SetWidgetLoweredState(i + WID_AID_COMPANY_BUTTON_START, ai_debug_company == i);
 
		}
 

	
 
		this->SetWidgetDisabledState(WID_AID_SCRIPT_GAME, Game::GetGameInstance() == nullptr);
 
		this->SetWidgetLoweredState(WID_AID_SCRIPT_GAME, ai_debug_company == OWNER_DEITY);
 

	
src/airport_gui.cpp
Show inline comments
 
@@ -50,49 +50,49 @@ void CcBuildAirport(const CommandCost &r
 
	if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
 
	if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
}
 

	
 
/**
 
 * Place an airport.
 
 * @param tile Position to put the new airport.
 
 */
 
static void PlaceAirport(TileIndex tile)
 
{
 
	if (_selected_airport_index == -1) return;
 
	uint32 p2 = _ctrl_pressed;
 
	SB(p2, 16, 16, INVALID_STATION); // no station to join
 

	
 
	uint32 p1 = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex();
 
	p1 |= _selected_airport_layout << 8;
 

	
 
	auto proc = [=](bool test, StationID to_join) -> bool {
 
		if (test) {
 
			return Command<CMD_BUILD_AIRPORT>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_AIRPORT>()), tile, p1, p2, {}).Succeeded();
 
		} else {
 
			uint32 p2_final = p2;
 
			if (to_join != INVALID_STATION) SB(p2_final, 16, 16, to_join);
 

	
 
			return DoCommandP(CMD_BUILD_AIRPORT, STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, p1, p2_final);
 
			return Command<CMD_BUILD_AIRPORT>::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, p1, p2_final, {});
 
		}
 
	};
 

	
 
	ShowSelectStationIfNeeded(TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE), proc);
 
}
 

	
 
/** Airport build toolbar window handler. */
 
struct BuildAirToolbarWindow : Window {
 
	int last_user_action; // Last started user action.
 

	
 
	BuildAirToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
 
	{
 
		this->InitNested(window_number);
 
		this->OnInvalidateData();
 
		if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this);
 
		this->last_user_action = WIDGET_LIST_END;
 
	}
 

	
 
	void Close() override
 
	{
 
		if (this->IsWidgetLowered(WID_AT_AIRPORT)) SetViewportCatchmentStation(nullptr, true);
 
		if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
 
		this->Window::Close();
 
	}
src/autoreplace_gui.cpp
Show inline comments
 
@@ -4,48 +4,51 @@
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file autoreplace_gui.cpp GUI for autoreplace handling. */
 

	
 
#include "stdafx.h"
 
#include "command_func.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 
#include "rail.h"
 
#include "road.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "autoreplace_func.h"
 
#include "company_func.h"
 
#include "engine_base.h"
 
#include "window_gui.h"
 
#include "engine_gui.h"
 
#include "settings_func.h"
 
#include "core/geometry_func.hpp"
 
#include "rail_gui.h"
 
#include "road_gui.h"
 
#include "widgets/dropdown_func.h"
 
#include "autoreplace_cmd.h"
 
#include "group_cmd.h"
 
#include "settings_cmd.h"
 

	
 
#include "widgets/autoreplace_widget.h"
 

	
 
#include "safeguards.h"
 

	
 
void DrawEngineList(VehicleType type, int x, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
 

	
 
static bool EngineNumberSorter(const EngineID &a, const EngineID &b)
 
{
 
	return Engine::Get(a)->list_position < Engine::Get(b)->list_position;
 
}
 

	
 
/**
 
 * Rebuild the left autoreplace list if an engine is removed or added
 
 * @param e Engine to check if it is removed or added
 
 * @param id_g The group the engine belongs to
 
 *  Note: this function only works if it is called either
 
 *   - when a new vehicle is build, but before it's counted in num_engines
 
 *   - when a vehicle is deleted and after it's subtracted from num_engines
 
 *   - when not changing the count (used when changing replace orders)
 
 */
 
void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g)
 
{
 
	if (GetGroupNumEngines(_local_company, id_g, e) == 0 || GetGroupNumEngines(_local_company, ALL_GROUP, e) == 0) {
 
@@ -196,49 +199,49 @@ class ReplaceVehicleWindow : public Wind
 
				if (this->reset_sel_engine && this->sel_engine[1] != INVALID_ENGINE) {
 
					int position = 0;
 
					for (EngineID &eid : this->engines[1]) {
 
						if (eid == this->sel_engine[1]) break;
 
						++position;
 
					}
 
					this->vscroll[1]->ScrollTowards(position);
 
				}
 
			}
 
		}
 
		/* Reset the flags about needed updates */
 
		this->engines[0].RebuildDone();
 
		this->engines[1].RebuildDone();
 
		this->reset_sel_engine = false;
 
	}
 

	
 
	/**
 
	 * Handle click on the start replace button.
 
	 * @param replace_when_old Replace now or only when old?
 
	 */
 
	void ReplaceClick_StartReplace(bool replace_when_old)
 
	{
 
		EngineID veh_from = this->sel_engine[0];
 
		EngineID veh_to = this->sel_engine[1];
 
		DoCommandP(CMD_SET_AUTOREPLACE, 0, (replace_when_old ? 1 : 0) | (this->sel_group << 16), veh_from + (veh_to << 16));
 
		Command<CMD_SET_AUTOREPLACE>::Post(0, (replace_when_old ? 1 : 0) | (this->sel_group << 16), veh_from + (veh_to << 16), {});
 
	}
 

	
 
public:
 
	ReplaceVehicleWindow(WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc)
 
	{
 
		this->sel_railtype = INVALID_RAILTYPE;
 
		this->sel_roadtype = INVALID_ROADTYPE;
 
		this->replace_engines  = true; // start with locomotives (all other vehicles will not read this bool)
 
		this->engines[0].ForceRebuild();
 
		this->engines[1].ForceRebuild();
 
		this->reset_sel_engine = true;
 
		this->details_height   = ((vehicletype == VEH_TRAIN) ? 10 : 9);
 
		this->sel_engine[0] = INVALID_ENGINE;
 
		this->sel_engine[1] = INVALID_ENGINE;
 
		this->show_hidden_engines = _engine_sort_show_hidden_engines[vehicletype];
 

	
 
		this->CreateNestedTree();
 
		this->vscroll[0] = this->GetScrollbar(WID_RV_LEFT_SCROLLBAR);
 
		this->vscroll[1] = this->GetScrollbar(WID_RV_RIGHT_SCROLLBAR);
 

	
 
		NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_RV_SHOW_HIDDEN_ENGINES);
 
		widget->widget_data = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + vehicletype;
 
		widget->tool_tip    = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + vehicletype;
 
		widget->SetLowered(this->show_hidden_engines);
 
@@ -520,92 +523,92 @@ public:
 

	
 
			case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: {
 
				DropDownList list;
 
				list.emplace_back(new DropDownListStringItem(STR_REPLACE_ENGINES, 1, false));
 
				list.emplace_back(new DropDownListStringItem(STR_REPLACE_WAGONS, 0, false));
 
				ShowDropDownList(this, std::move(list), this->replace_engines ? 1 : 0, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN);
 
				break;
 
			}
 

	
 
			case WID_RV_RAIL_ROAD_TYPE_DROPDOWN: // Rail/roadtype selection dropdown menu
 
				switch (this->window_number) {
 
					case VEH_TRAIN:
 
						ShowDropDownList(this, GetRailTypeDropDownList(true, true), sel_railtype, WID_RV_RAIL_ROAD_TYPE_DROPDOWN);
 
						break;
 

	
 
					case VEH_ROAD:
 
						ShowDropDownList(this, GetRoadTypeDropDownList(RTTB_ROAD | RTTB_TRAM, true, true), sel_roadtype, WID_RV_RAIL_ROAD_TYPE_DROPDOWN);
 
						break;
 
				}
 
				break;
 

	
 
			case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
 
				const Group *g = Group::GetIfValid(this->sel_group);
 
				if (g != nullptr) {
 
					DoCommandP(CMD_SET_GROUP_FLAG, 0, this->sel_group | (GroupFlags::GF_REPLACE_WAGON_REMOVAL << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL) ? 0 : 1) | (_ctrl_pressed << 1));
 
					Command<CMD_SET_GROUP_FLAG>::Post(0, this->sel_group | (GroupFlags::GF_REPLACE_WAGON_REMOVAL << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL) ? 0 : 1) | (_ctrl_pressed << 1), {});
 
				} else {
 
					// toggle renew_keep_length
 
					DoCommandP(CMD_CHANGE_COMPANY_SETTING, 0, 0, Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, "company.renew_keep_length");
 
					Command<CMD_CHANGE_COMPANY_SETTING>::Post(0, 0, Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, "company.renew_keep_length");
 
				}
 
				break;
 
			}
 

	
 
			case WID_RV_START_REPLACE: { // Start replacing
 
				if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
 
					this->HandleButtonClick(WID_RV_START_REPLACE);
 
					ReplaceClick_StartReplace(false);
 
				} else {
 
					bool replacment_when_old = EngineHasReplacementWhenOldForCompany(Company::Get(_local_company), this->sel_engine[0], this->sel_group);
 
					ShowDropDownMenu(this, _start_replace_dropdown, replacment_when_old ? 1 : 0, WID_RV_START_REPLACE, !this->replace_engines ? 1 << 1 : 0, 0);
 
				}
 
				break;
 
			}
 

	
 
			case WID_RV_STOP_REPLACE: { // Stop replacing
 
				EngineID veh_from = this->sel_engine[0];
 
				DoCommandP(CMD_SET_AUTOREPLACE, 0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16));
 
				Command<CMD_SET_AUTOREPLACE>::Post(0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16), {});
 
				break;
 
			}
 

	
 
			case WID_RV_LEFT_MATRIX:
 
			case WID_RV_RIGHT_MATRIX: {
 
				byte click_side;
 
				if (widget == WID_RV_LEFT_MATRIX) {
 
					click_side = 0;
 
				} else {
 
					click_side = 1;
 
				}
 
				uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
 
				size_t engine_count = this->engines[click_side].size();
 

	
 
				EngineID e = engine_count > i ? this->engines[click_side][i] : INVALID_ENGINE;
 

	
 
				/* If Ctrl is pressed on the left side and we don't have any engines of the selected type, stop autoreplacing.
 
				 * This is most common when we have finished autoreplacing the engine and want to remove it from the list. */
 
				if (click_side == 0 && _ctrl_pressed && e != INVALID_ENGINE &&
 
					(GetGroupNumEngines(_local_company, sel_group, e) == 0 || GetGroupNumEngines(_local_company, ALL_GROUP, e) == 0)) {
 
						EngineID veh_from = e;
 
						DoCommandP(CMD_SET_AUTOREPLACE, 0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16));
 
						Command<CMD_SET_AUTOREPLACE>::Post(0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16), {});
 
						break;
 
				}
 

	
 
				if (e == this->sel_engine[click_side]) break; // we clicked the one we already selected
 
				this->sel_engine[click_side] = e;
 
				if (click_side == 0) {
 
					this->engines[1].ForceRebuild();
 
					this->reset_sel_engine = true;
 
				}
 
				this->SetDirty();
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		switch (widget) {
 
			case WID_RV_SORT_DROPDOWN:
 
				if (this->sort_criteria != index) {
 
					this->sort_criteria = index;
 
					_engine_sort_last_criteria[this->window_number] = this->sort_criteria;
 
					this->engines[1].ForceRebuild();
 
					this->SetDirty();
src/bridge_gui.cpp
Show inline comments
 
@@ -98,50 +98,50 @@ private:
 
	static bool BridgeIndexSorter(const BuildBridgeData &a, const BuildBridgeData &b)
 
	{
 
		return a.index < b.index;
 
	}
 

	
 
	/** Sort the bridges by their price */
 
	static bool BridgePriceSorter(const BuildBridgeData &a, const BuildBridgeData &b)
 
	{
 
		return a.cost < b.cost;
 
	}
 

	
 
	/** Sort the bridges by their maximum speed */
 
	static bool BridgeSpeedSorter(const BuildBridgeData &a, const BuildBridgeData &b)
 
	{
 
		return a.spec->speed < b.spec->speed;
 
	}
 

	
 
	void BuildBridge(uint8 i)
 
	{
 
		switch ((TransportType)(this->type >> 15)) {
 
			case TRANSPORT_RAIL: _last_railbridge_type = this->bridges->at(i).index; break;
 
			case TRANSPORT_ROAD: _last_roadbridge_type = this->bridges->at(i).index; break;
 
			default: break;
 
		}
 
		DoCommandP(CMD_BUILD_BRIDGE, STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, CcBuildBridge,
 
					this->end_tile, this->start_tile, this->type | this->bridges->at(i).index);
 
		Command<CMD_BUILD_BRIDGE>::Post(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, CcBuildBridge,
 
					this->end_tile, this->start_tile, this->type | this->bridges->at(i).index, {});
 
	}
 

	
 
	/** Sort the builable bridges */
 
	void SortBridgeList()
 
	{
 
		this->bridges->Sort();
 

	
 
		/* Display the current sort variant */
 
		this->GetWidget<NWidgetCore>(WID_BBS_DROPDOWN_CRITERIA)->widget_data = this->sorter_names[this->bridges->SortType()];
 

	
 
		/* Set the modified widgets dirty */
 
		this->SetWidgetDirty(WID_BBS_DROPDOWN_CRITERIA);
 
		this->SetWidgetDirty(WID_BBS_BRIDGE_LIST);
 
	}
 

	
 
public:
 
	BuildBridgeWindow(WindowDesc *desc, TileIndex start, TileIndex end, uint32 br_type, GUIBridgeList *bl) : Window(desc),
 
		start_tile(start),
 
		end_tile(end),
 
		type(br_type),
 
		bridges(bl)
 
	{
 
		this->CreateNestedTree();
 
		this->vscroll = this->GetScrollbar(WID_BBS_SCROLLBAR);
 
@@ -364,49 +364,49 @@ void ShowBuildBridgeWindow(TileIndex sta
 
{
 
	CloseWindowByClass(WC_BUILD_BRIDGE);
 

	
 
	/* Data type for the bridge.
 
	 * Bit 16,15 = transport type,
 
	 *     14..8 = road/rail types,
 
	 *      7..0 = type of bridge */
 
	uint32 type = (transport_type << 15) | (road_rail_type << 8);
 

	
 
	/* The bridge length without ramps. */
 
	const uint bridge_len = GetTunnelBridgeLength(start, end);
 

	
 
	/* If Ctrl is being pressed, check whether the last bridge built is available
 
	 * If so, return this bridge type. Otherwise continue normally.
 
	 * We store bridge types for each transport type, so we have to check for
 
	 * the transport type beforehand.
 
	 */
 
	BridgeType last_bridge_type = 0;
 
	switch (transport_type) {
 
		case TRANSPORT_ROAD: last_bridge_type = _last_roadbridge_type; break;
 
		case TRANSPORT_RAIL: last_bridge_type = _last_railbridge_type; break;
 
		default: break; // water ways and air routes don't have bridge types
 
	}
 
	if (_ctrl_pressed && CheckBridgeAvailability(last_bridge_type, bridge_len).Succeeded()) {
 
		DoCommandP(CMD_BUILD_BRIDGE, STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, CcBuildBridge, end, start, type | last_bridge_type);
 
		Command<CMD_BUILD_BRIDGE>::Post(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, CcBuildBridge, end, start, type | last_bridge_type, {});
 
		return;
 
	}
 

	
 
	/* only query bridge building possibility once, result is the same for all bridges!
 
	 * returns CMD_ERROR on failure, and price on success */
 
	StringID errmsg = INVALID_STRING_ID;
 
	CommandCost ret = Command<CMD_BUILD_BRIDGE>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_BRIDGE>()) | DC_QUERY_COST, end, start, type, {});
 

	
 
	GUIBridgeList *bl = nullptr;
 
	if (ret.Failed()) {
 
		errmsg = ret.GetErrorMessage();
 
	} else {
 
		/* check which bridges can be built */
 
		const uint tot_bridgedata_len = CalcBridgeLenCostFactor(bridge_len + 2);
 

	
 
		bl = new GUIBridgeList();
 

	
 
		Money infra_cost = 0;
 
		switch (transport_type) {
 
			case TRANSPORT_ROAD: {
 
				/* In case we add a new road type as well, we must be aware of those costs. */
 
				RoadType road_rt = INVALID_ROADTYPE;
 
				RoadType tram_rt = INVALID_ROADTYPE;
 
				if (IsBridgeTile(start)) {
src/build_vehicle_gui.cpp
Show inline comments
 
@@ -9,48 +9,49 @@
 

	
 
#include "stdafx.h"
 
#include "engine_base.h"
 
#include "engine_func.h"
 
#include "station_base.h"
 
#include "network/network.h"
 
#include "articulated_vehicles.h"
 
#include "textbuf_gui.h"
 
#include "command_func.h"
 
#include "company_func.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_text.h"
 
#include "group.h"
 
#include "string_func.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "date_func.h"
 
#include "vehicle_func.h"
 
#include "widgets/dropdown_func.h"
 
#include "engine_gui.h"
 
#include "cargotype.h"
 
#include "core/geometry_func.hpp"
 
#include "autoreplace_func.h"
 
#include "engine_cmd.h"
 
#include "train_cmd.h"
 
#include "vehicle_cmd.h"
 

	
 
#include "widgets/build_vehicle_widget.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/**
 
 * Get the height of a single 'entry' in the engine lists.
 
 * @param type the vehicle type to get the height of
 
 * @return the height for the entry
 
 */
 
uint GetEngineListHeight(VehicleType type)
 
{
 
	return std::max<uint>(FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM, GetVehicleImageCellSize(type, EIT_PURCHASE).height);
 
}
 

	
 
static const NWidgetPart _nested_build_vehicle_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_GREY),
 
		NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_SHADEBOX, COLOUR_GREY),
 
@@ -1439,60 +1440,60 @@ struct BuildVehicleWindow : Window {
 
			case WID_BV_LIST: {
 
				uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST);
 
				size_t num_items = this->eng_list.size();
 
				this->SelectEngine((i < num_items) ? this->eng_list[i] : INVALID_ENGINE);
 
				this->SetDirty();
 
				if (_ctrl_pressed) {
 
					this->OnClick(pt, WID_BV_SHOW_HIDE, 1);
 
				} else if (click_count > 1 && !this->listview_mode) {
 
					this->OnClick(pt, WID_BV_BUILD, 1);
 
				}
 
				break;
 
			}
 

	
 
			case WID_BV_SORT_DROPDOWN: // Select sorting criteria dropdown menu
 
				DisplayVehicleSortDropDown(this, this->vehicle_type, this->sort_criteria, WID_BV_SORT_DROPDOWN);
 
				break;
 

	
 
			case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
 
				ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, WID_BV_CARGO_FILTER_DROPDOWN, 0, 0);
 
				break;
 

	
 
			case WID_BV_SHOW_HIDE: {
 
				const Engine *e = (this->sel_engine == INVALID_ENGINE) ? nullptr : Engine::Get(this->sel_engine);
 
				if (e != nullptr) {
 
					DoCommandP(CMD_SET_VEHICLE_VISIBILITY, 0, 0, this->sel_engine | (e->IsHidden(_current_company) ? 0 : (1u << 31)));
 
					Command<CMD_SET_VEHICLE_VISIBILITY>::Post(0, 0, this->sel_engine | (e->IsHidden(_current_company) ? 0 : (1u << 31)), {});
 
				}
 
				break;
 
			}
 

	
 
			case WID_BV_BUILD: {
 
				EngineID sel_eng = this->sel_engine;
 
				if (sel_eng != INVALID_ENGINE) {
 
					CommandCallback *callback = (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildPrimaryVehicle;
 
					CargoID cargo = this->cargo_filter[this->cargo_filter_criteria];
 
					if (cargo == CF_ANY || cargo == CF_ENGINES) cargo = CF_NONE;
 
					DoCommandP(CMD_BUILD_VEHICLE, GetCmdBuildVehMsg(this->vehicle_type), callback, this->window_number, sel_eng | (cargo << 24), 0);
 
					Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), callback, this->window_number, sel_eng | (cargo << 24), 0, {});
 
				}
 
				break;
 
			}
 

	
 
			case WID_BV_RENAME: {
 
				EngineID sel_eng = this->sel_engine;
 
				if (sel_eng != INVALID_ENGINE) {
 
					this->rename_engine = sel_eng;
 
					SetDParam(0, sel_eng);
 
					ShowQueryString(STR_ENGINE_NAME, STR_QUERY_RENAME_TRAIN_TYPE_CAPTION + this->vehicle_type, MAX_LENGTH_ENGINE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
	/**
 
	 * Some data on this window has become invalid.
 
	 * @param data Information about the changed data.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	void OnInvalidateData(int data = 0, bool gui_scope = true) override
 
	{
 
		if (!gui_scope) return;
 
@@ -1615,49 +1616,49 @@ struct BuildVehicleWindow : Window {
 
		this->DrawWidgets();
 

	
 
		if (!this->IsShaded()) {
 
			int needed_height = this->details_height;
 
			/* Draw details panels. */
 
			if (this->sel_engine != INVALID_ENGINE) {
 
				NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_BV_PANEL);
 
				int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
 
						nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine, this->te);
 
				needed_height = std::max(needed_height, (text_end - (int)nwi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL);
 
			}
 
			if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
 
				int resize = needed_height - this->details_height;
 
				this->details_height = needed_height;
 
				this->ReInit(0, resize * FONT_HEIGHT_NORMAL);
 
				return;
 
			}
 
		}
 
	}
 

	
 
	void OnQueryTextFinished(char *str) override
 
	{
 
		if (str == nullptr) return;
 

	
 
		DoCommandP(CMD_RENAME_ENGINE, STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, 0, this->rename_engine, 0, str);
 
		Command<CMD_RENAME_ENGINE>::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, 0, this->rename_engine, 0, str);
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		switch (widget) {
 
			case WID_BV_SORT_DROPDOWN:
 
				if (this->sort_criteria != index) {
 
					this->sort_criteria = index;
 
					_engine_sort_last_criteria[this->vehicle_type] = this->sort_criteria;
 
					this->eng_list.ForceRebuild();
 
				}
 
				break;
 

	
 
			case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
 
				if (this->cargo_filter_criteria != index) {
 
					this->cargo_filter_criteria = index;
 
					_engine_sort_last_cargo_criteria[this->vehicle_type] = this->cargo_filter[this->cargo_filter_criteria];
 
					/* deactivate filter if criteria is 'Show All', activate it otherwise */
 
					this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
 
					this->eng_list.ForceRebuild();
 
					this->SelectEngine(this->sel_engine);
 
				}
 
				break;
 
		}
src/cheat_gui.cpp
Show inline comments
 
@@ -7,75 +7,76 @@
 

	
 
/** @file cheat_gui.cpp GUI related to cheating. */
 

	
 
#include "stdafx.h"
 
#include "command_func.h"
 
#include "cheat_type.h"
 
#include "company_base.h"
 
#include "company_func.h"
 
#include "date_func.h"
 
#include "saveload/saveload.h"
 
#include "vehicle_base.h"
 
#include "textbuf_gui.h"
 
#include "window_gui.h"
 
#include "string_func.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "rail_gui.h"
 
#include "settings_gui.h"
 
#include "company_gui.h"
 
#include "linkgraph/linkgraphschedule.h"
 
#include "map_func.h"
 
#include "tile_map.h"
 
#include "newgrf.h"
 
#include "error.h"
 
#include "misc_cmd.h"
 

	
 
#include "widgets/cheat_widget.h"
 

	
 
#include "table/sprites.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
/**
 
 * The 'amount' to cheat with.
 
 * This variable is semantically a constant value, but because the cheat
 
 * code requires to be able to write to the variable it is not constified.
 
 */
 
static int32 _money_cheat_amount = 10000000;
 

	
 
/**
 
 * Handle cheating of money.
 
 * Note that the amount of money of a company must be changed through a command
 
 * rather than by setting a variable. Since the cheat data structure expects a
 
 * variable, the amount of given/taken money is used for this purpose.
 
 * @param p1 not used.
 
 * @param p2 is -1 or +1 (down/up)
 
 * @return Amount of money cheat.
 
 */
 
static int32 ClickMoneyCheat(int32 p1, int32 p2)
 
{
 
	DoCommandP(CMD_MONEY_CHEAT, 0, (uint32)(p2 * _money_cheat_amount), 0);
 
	Command<CMD_MONEY_CHEAT>::Post(0, (uint32)(p2 * _money_cheat_amount), 0, {});
 
	return _money_cheat_amount;
 
}
 

	
 
/**
 
 * Handle changing of company.
 
 * @param p1 company to set to
 
 * @param p2 is -1 or +1 (down/up)
 
 * @return The new company.
 
 */
 
static int32 ClickChangeCompanyCheat(int32 p1, int32 p2)
 
{
 
	while ((uint)p1 < Company::GetPoolSize()) {
 
		if (Company::IsValidID((CompanyID)p1)) {
 
			SetLocalCompany((CompanyID)p1);
 
			return _local_company;
 
		}
 
		p1 += p2;
 
	}
 

	
 
	return _local_company;
 
}
 

	
 
/**
 
 * Allow (or disallow) changing production of all industries.
src/command.cpp
Show inline comments
 
@@ -192,197 +192,107 @@ void CommandHelperBase::InternalDoBefore
 

	
 
/**
 
 * Process result after calling a command proc.
 
 * @param[in,out] res Command result, may be modified.
 
 * @param flags Command flags.
 
 * @param top_level Top level of command execution, i.e. command from a command.
 
 * @param test Test run of command?
 
 */
 
void CommandHelperBase::InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test)
 
{
 
	if (test) {
 
		SetTownRatingTestMode(false);
 

	
 
		if (res.Succeeded() && top_level && !(flags & DC_QUERY_COST) && !(flags & DC_BANKRUPT)) {
 
			CheckCompanyHasMoney(res); // CheckCompanyHasMoney() modifies 'res' to an error if it fails.
 
		}
 
	} else {
 
		/* If top-level, subtract the money. */
 
		if (res.Succeeded() && top_level && !(flags & DC_BANKRUPT)) {
 
			SubtractMoneyFromCompany(res);
 
		}
 
	}
 
}
 

	
 
/*!
 
 * Toplevel network safe docommand function for the current company. Must not be called recursively.
 
 * The callback is called when the command succeeded or failed. The parameters
 
 * \a tile, \a p1, and \a p2 are from the #CommandProc function. The parameter \a cmd is the command to execute.
 
 * The parameter \a my_cmd is used to indicate if the command is from a company or the server.
 
 *
 
 * @param cmd The command to execute (a CMD_* value)
 
 * @param callback A callback function to call after the command is finished
 
 * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
 
 * @param network_command execute the command without sending it on the network
 
 * @param tile The tile to perform a command on (see #CommandProc)
 
 * @param p1 Additional data for the command (see #CommandProc)
 
 * @param p2 Additional data for the command (see #CommandProc)
 
 * @param text The text to pass
 
 * @return \c true if the command succeeded, else \c false.
 
/**
 
 * Decide what to do with the command depending on current game state.
 
 * @param cmd Command to execute.
 
 * @param flags Command flags.
 
 * @param tile Tile of command execution.
 
 * @param err_message Message prefix to show on error.
 
 * @param network_command Does this command come from the network?
 
 * @return error state + do only cost estimation? + send to network only?
 
 */
 
static bool DoCommandP(Commands cmd, StringID err_message, CommandCallback *callback, bool my_cmd, bool network_command, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
std::tuple<bool, bool, bool> CommandHelperBase::InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command)
 
{
 
	/* Cost estimation is generally only done when the
 
	 * local user presses shift while doing something.
 
	 * However, in case of incoming network commands,
 
	 * map generation or the pause button we do want
 
	 * to execute. */
 
	bool estimate_only = _shift_pressed && IsLocalCompany() &&
 
			!_generating_world &&
 
			!network_command &&
 
			!(GetCommandFlags(cmd) & CMD_NO_EST);
 
	bool estimate_only = _shift_pressed && IsLocalCompany() && !_generating_world && !network_command && !(flags & CMD_NO_EST);
 

	
 
	/* We're only sending the command, so don't do
 
	 * fancy things for 'success'. */
 
	bool only_sending = _networking && !network_command;
 

	
 
	/* Where to show the message? */
 
	if (_pause_mode != PM_UNPAUSED && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) {
 
		ShowErrorMessage(err_message, STR_ERROR_NOT_ALLOWED_WHILE_PAUSED, WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
 
		return { true, estimate_only, only_sending };
 
	} else {
 
		return { false, estimate_only, only_sending };
 
	}
 
}
 

	
 
/**
 
 * Process result of executing a command, possibly displaying any error to the player.
 
 * @param res Command result.
 
 * @param tile Tile of command execution.
 
 * @param estimate_only Is this just cost estimation?
 
 * @param only_sending Was the command only sent to network?
 
 * @param err_message Message prefix to show on error.
 
 * @param my_cmd Is the command from this client?
 
 */
 
void CommandHelperBase::InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd)
 
{
 
	int x = TileX(tile) * TILE_SIZE;
 
	int y = TileY(tile) * TILE_SIZE;
 

	
 
	if (_pause_mode != PM_UNPAUSED && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) {
 
		ShowErrorMessage(err_message, STR_ERROR_NOT_ALLOWED_WHILE_PAUSED, WL_INFO, x, y);
 
		return false;
 
	}
 

	
 
	/* Only set p2 when the command does not come from the network. */
 
	if (!network_command && GetCommandFlags(cmd) & CMD_CLIENT_ID && p2 == 0) p2 = CLIENT_ID_SERVER;
 

	
 
	CommandCost res = DoCommandPInternal(cmd, err_message, callback, my_cmd, estimate_only, network_command, tile, p1, p2, text);
 
	if (res.Failed()) {
 
		/* Only show the error when it's for us. */
 
		if (estimate_only || (IsLocalCompany() && err_message != 0 && my_cmd)) {
 
			ShowErrorMessage(err_message, res.GetErrorMessage(), WL_INFO, x, y, res.GetTextRefStackGRF(), res.GetTextRefStackSize(), res.GetTextRefStack());
 
		}
 
	} else if (estimate_only) {
 
		ShowEstimatedCostOrIncome(res.GetCost(), x, y);
 
	} else if (!only_sending && res.GetCost() != 0 && tile != 0 && IsLocalCompany() && _game_mode != GM_EDITOR) {
 
		/* Only show the cost animation when we did actually
 
		 * execute the command, i.e. we're not sending it to
 
		 * the server, when it has cost the local company
 
		 * something. Furthermore in the editor there is no
 
		 * concept of cost, so don't show it there either. */
 
		ShowCostOrIncomeAnimation(x, y, GetSlopePixelZ(x, y), res.GetCost());
 
	}
 

	
 
	if (!estimate_only && !only_sending && callback != nullptr) {
 
		callback(res, cmd, tile, p1, p2, text);
 
	}
 

	
 
	return res.Succeeded();
 
}
 

	
 
/**
 
 * Shortcut for the long DoCommandP when not using a callback or error message.
 
 * @param cmd The command to execute (a CMD_* value)
 
 * @param tile The tile to perform a command on (see #CommandProc)
 
 * @param p1 Additional data for the command (see #CommandProc)
 
 * @param p2 Additional data for the command (see #CommandProc)
 
 * @param text The text to pass
 
 * @return \c true if the command succeeded, else \c false.
 
 */
 
bool DoCommandP(Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	return DoCommandP(cmd, STR_NULL, nullptr, true, false, tile, p1, p2, text);
 
}
 

	
 
/**
 
 * Shortcut for the long DoCommandP when not using an error message.
 
 * @param cmd The command to execute (a CMD_* value)
 
 * @param callback A callback function to call after the command is finished
 
 * @param tile The tile to perform a command on (see #CommandProc)
 
 * @param p1 Additional data for the command (see #CommandProc)
 
 * @param p2 Additional data for the command (see #CommandProc)
 
 * @param text The text to pass
 
 * @return \c true if the command succeeded, else \c false.
 
 */
 
bool DoCommandP(Commands cmd, CommandCallback *callback, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	return DoCommandP(cmd, STR_NULL, callback, true, false, tile, p1, p2, text);
 
}
 

	
 
/**
 
 * Shortcut for the long DoCommandP when not using a callback.
 
 * @param cmd The command to execute (a CMD_* value)
 
 * @param err_message Message prefix to show on error
 
 * @param tile The tile to perform a command on (see #CommandProc)
 
 * @param p1 Additional data for the command (see #CommandProc)
 
 * @param p2 Additional data for the command (see #CommandProc)
 
 * @param text The text to pass
 
 * @return \c true if the command succeeded, else \c false.
 
 */
 
bool DoCommandP(Commands cmd, StringID err_message, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	return DoCommandP(cmd, err_message, nullptr, true, false, tile, p1, p2, text);
 
}
 

	
 
/*!
 
 * Toplevel network safe docommand function for the current company. Must not be called recursively.
 
 * The callback is called when the command succeeded or failed. The parameters
 
 * \a tile, \a p1, and \a p2 are from the #CommandProc function. The parameter \a cmd is the command to execute.
 
 *
 
 * @param cmd The command to execute (a CMD_* value)
 
 * @param err_message Message prefix to show on error
 
 * @param callback A callback function to call after the command is finished
 
 * @param tile The tile to perform a command on (see #CommandProc)
 
 * @param p1 Additional data for the command (see #CommandProc)
 
 * @param p2 Additional data for the command (see #CommandProc)
 
 * @param text The text to pass
 
 * @return \c true if the command succeeded, else \c false.
 
 */
 
bool DoCommandP(Commands cmd, StringID err_message, CommandCallback *callback, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	return DoCommandP(cmd, err_message, callback, true, false, tile, p1, p2, text);
 
}
 

	
 
/**
 
 * Toplevel network safe docommand function for the current company. Must not be called recursively.
 
 * The callback is called when the command succeeded or failed. The parameters
 
 * \a tile, \a p1, and \a p2 are from the #CommandProc function. The parameter \a cmd is the command to execute.
 
 *
 
 * @param cmd The command to execute (a CMD_* value)
 
 * @param err_message Message prefix to show on error
 
 * @param callback A callback function to call after the command is finished
 
 * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
 
 * @param tile The tile to perform a command on (see #CommandProc)
 
 * @param p1 Additional data for the command (see #CommandProc)
 
 * @param p2 Additional data for the command (see #CommandProc)
 
 * @param text The text to pass
 
 * @return \c true if the command succeeded, else \c false.
 
 */
 
bool InjectNetworkCommand(Commands cmd, StringID err_message, CommandCallback *callback, bool my_cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	return DoCommandP(cmd, err_message, callback, my_cmd, true, tile, p1, p2, text);
 
}
 

	
 
/** Helper to format command parameters into a hex string. */
 
static std::string CommandParametersToHexString(TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	return FormatArrayAsHex(EndianBufferWriter<>::FromValue(std::make_tuple(tile, p1, p2, text)));
 
}
 

	
 
/*!
 
 * Helper function for the toplevel network safe docommand function for the current company.
 
 *
 
 * @param cmd The command to execute (a CMD_* value)
 
 * @param err_message Message prefix to show on error
 
 * @param callback A callback function to call after the command is finished
 
 * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
 
 * @param estimate_only whether to give only the estimate or also execute the command
 
 * @param tile The tile to perform a command on (see #CommandProc)
 
 * @param p1 Additional data for the command (see #CommandProc)
 
 * @param p2 Additional data for the command (see #CommandProc)
 
 * @param text The text to pass
 
 * @return the command cost of this function.
 
 */
 
CommandCost DoCommandPInternal(Commands cmd, StringID err_message, CommandCallback *callback, bool my_cmd, bool estimate_only, bool network_command, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
src/command_func.h
Show inline comments
 
@@ -16,56 +16,48 @@
 
#include "tile_map.h"
 

	
 
/**
 
 * Define a default return value for a failed command.
 
 *
 
 * This variable contains a CommandCost object with is declared as "failed".
 
 * Other functions just need to return this error if there is an error,
 
 * which doesn't need to specific by a StringID.
 
 */
 
static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID);
 

	
 
/**
 
 * Returns from a function with a specific StringID as error.
 
 *
 
 * This macro is used to return from a function. The parameter contains the
 
 * StringID which will be returned.
 
 *
 
 * @param errcode The StringID to return
 
 */
 
#define return_cmd_error(errcode) return CommandCost(errcode);
 

	
 
/** Storage buffer for serialized command data. */
 
typedef std::vector<byte> CommandDataBuffer;
 

	
 

	
 
bool DoCommandP(Commands cmd, StringID err_message, CommandCallback *callback, TileIndex tile, uint32 p1, uint32 p2, const std::string &text = {});
 
bool DoCommandP(Commands cmd, StringID err_message, TileIndex tile, uint32 p1, uint32 p2, const std::string &text = {});
 
bool DoCommandP(Commands cmd, CommandCallback *callback, TileIndex tile, uint32 p1, uint32 p2, const std::string &text = {});
 
bool DoCommandP(Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text = {});
 

	
 
bool InjectNetworkCommand(Commands cmd, StringID err_message, CommandCallback *callback, bool my_cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text);
 

	
 
CommandCost DoCommandPInternal(Commands cmd, StringID err_message, CommandCallback *callback, bool my_cmd, bool estimate_only, bool network_command, TileIndex tile, uint32 p1, uint32 p2, const std::string &text);
 

	
 
void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex tile, uint32 p1, uint32 p2, const std::string &text);
 
void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex location, const CommandDataBuffer &cmd_data);
 

	
 
extern Money _additional_cash_required;
 

	
 
bool IsValidCommand(Commands cmd);
 
CommandFlags GetCommandFlags(Commands cmd);
 
const char *GetCommandName(Commands cmd);
 
Money GetAvailableMoneyForCommand();
 
bool IsCommandAllowedWhilePaused(Commands cmd);
 

	
 
template <Commands Tcmd>
 
constexpr CommandFlags GetCommandFlags()
 
{
 
	return CommandTraits<Tcmd>::flags;
 
}
 

	
 
/**
 
 * Extracts the DC flags needed for DoCommand from the flags returned by GetCommandFlags
 
 * @param cmd_flags Flags from GetCommandFlags
 
 * @return flags for DoCommand
 
 */
 
@@ -75,48 +67,50 @@ static constexpr inline DoCommandFlag Co
 
	if (cmd_flags & CMD_NO_WATER) flags |= DC_NO_WATER;
 
	if (cmd_flags & CMD_AUTO) flags |= DC_AUTO;
 
	if (cmd_flags & CMD_ALL_TILES) flags |= DC_ALL_TILES;
 
	return flags;
 
}
 

	
 
/** Helper class to keep track of command nesting level. */
 
struct RecursiveCommandCounter {
 
	RecursiveCommandCounter() noexcept { _counter++; }
 
	~RecursiveCommandCounter() noexcept { _counter--; }
 

	
 
	/** Are we in the top-level command execution? */
 
	bool IsTopLevel() const { return _counter == 1; }
 
private:
 
	static int _counter;
 
};
 

	
 

	
 
template<Commands TCmd, typename T> struct CommandHelper;
 

	
 
class CommandHelperBase {
 
protected:
 
	static void InternalDoBefore(bool top_level, bool test);
 
	static void InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test);
 
	static std::tuple<bool, bool, bool> InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command);
 
	static void InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd);
 
};
 

	
 
/**
 
 * Templated wrapper that exposes the command parameter arguments
 
 * for the various Command::Do/Post calls.
 
 * @tparam Tcmd The command-id to execute.
 
 * @tparam Targs The command parameter types.
 
 */
 
template <Commands Tcmd, typename... Targs>
 
struct CommandHelper<Tcmd, CommandCost(*)(DoCommandFlag, Targs...)> : protected CommandHelperBase {
 
public:
 
	/**
 
	 * This function executes a given command with the parameters from the #CommandProc parameter list.
 
	 * Depending on the flags parameter it executes or tests a command.
 
	 *
 
	 * @note This function is to be called from the StateGameLoop or from within the execution of a Command.
 
	 * This function must not be called from the context of a "player" (real person, AI, game script).
 
	 * Use ::Post for commands directly triggered by "players".
 
	 *
 
	 * @param flags Flags for the command and how to execute the command
 
	 * @param args Parameters for the command
 
	 * @see CommandProc
 
	 * @return the cost
 
	 */
 
@@ -126,30 +120,107 @@ public:
 
			/* Do not even think about executing out-of-bounds tile-commands. */
 
			TileIndex tile = std::get<0>(std::make_tuple(args...));
 
			if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (flags & DC_ALL_TILES) == 0))) return CMD_ERROR;
 
		}
 

	
 
		RecursiveCommandCounter counter{};
 

	
 
		/* Only execute the test call if it's toplevel, or we're not execing. */
 
		if (counter.IsTopLevel() || !(flags & DC_EXEC)) {
 
			InternalDoBefore(counter.IsTopLevel(), true);
 
			CommandCost res = CommandTraits<Tcmd>::proc(flags & ~DC_EXEC, args...);
 
			InternalDoAfter(res, flags, counter.IsTopLevel(), true); // Can modify res.
 

	
 
			if (res.Failed() || !(flags & DC_EXEC)) return res;
 
		}
 

	
 
		/* Execute the command here. All cost-relevant functions set the expenses type
 
		 * themselves to the cost object at some point. */
 
		InternalDoBefore(counter.IsTopLevel(), false);
 
		CommandCost res = CommandTraits<Tcmd>::proc(flags, args...);
 
		InternalDoAfter(res, flags, counter.IsTopLevel(), false);
 

	
 
		return res;
 
	}
 

	
 
	/**
 
	 * Shortcut for the long Post when not using a callback.
 
	 * @param err_message Message prefix to show on error
 
	 * @param args Parameters for the command
 
	 */
 
	static inline bool Post(StringID err_message, Targs... args) { return Post(err_message, nullptr, std::forward<Targs>(args)...); }
 
	/**
 
	 * Shortcut for the long Post when not using an error message.
 
	 * @param callback A callback function to call after the command is finished
 
	 * @param args Parameters for the command
 
	 */
 
	static inline bool Post(CommandCallback *callback, Targs... args) { return Post((StringID)0, callback, std::forward<Targs>(args)...); }
 
	/**
 
	 * Shortcut for the long Post when not using a callback or an error message.
 
	 * @param args Parameters for the command
 
	 */
 
	static inline bool Post(Targs... args) { return Post((StringID)0, nullptr, std::forward<Targs>(args)...); }
 

	
 
	/**
 
	 * Top-level network safe command execution for the current company.
 
	 * Must not be called recursively. The callback is called when the
 
	 * command succeeded or failed.
 
	 *
 
	 * @param err_message Message prefix to show on error
 
	 * @param callback A callback function to call after the command is finished
 
	 * @param args Parameters for the command
 
	 * @return \c true if the command succeeded, else \c false.
 
	 */
 
	static bool Post(StringID err_message, CommandCallback *callback, Targs... args)
 
	{
 
		return InternalPost(err_message, callback, true, false, std::forward_as_tuple(args...));
 
	}
 

	
 
	/**
 
	 * Execute a command coming from the network.
 
	 * @param err_message Message prefix to show on error
 
	 * @param callback A callback function to call after the command is finished
 
	 * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
 
	 * @param location Tile location for user feedback.
 
	 * @param args Parameters for the command
 
	 * @return \c true if the command succeeded, else \c false.
 
	 */
 
	static bool PostFromNet(StringID err_message, CommandCallback *callback, bool my_cmd, TileIndex location, std::tuple<Targs...> args)
 
	{
 
		return InternalPost(err_message, callback, my_cmd, true, location, std::move(args));
 
	}
 

	
 
protected:
 
	static bool InternalPost(StringID err_message, CommandCallback *callback, bool my_cmd, bool network_command, std::tuple<Targs...> args)
 
	{
 
		/* Where to show the message? */
 
		TileIndex tile{};
 
		if constexpr (std::is_same_v<TileIndex, std::tuple_element_t<0, decltype(args)>>) {
 
			tile = std::get<0>(args);
 
		}
 

	
 
		return InternalPost(err_message, callback, my_cmd, network_command, tile, std::move(args));
 
	}
 

	
 
	static bool InternalPost(StringID err_message, CommandCallback *callback, bool my_cmd, bool network_command, TileIndex tile, std::tuple<Targs...> args)
 
	{
 
		auto [err, estimate_only, only_sending] = InternalPostBefore(Tcmd, GetCommandFlags<Tcmd>(), tile, err_message, network_command);
 
		if (err) return false;
 

	
 
		/* Only set p2 when the command does not come from the network. */
 
		if (!network_command && GetCommandFlags<Tcmd>() & CMD_CLIENT_ID && std::get<2>(args) == 0) std::get<2>(args) = CLIENT_ID_SERVER;
 

	
 
		CommandCost res = std::apply(DoCommandPInternal, std::tuple_cat(std::make_tuple(Tcmd, err_message, callback, my_cmd, estimate_only, network_command), args));
 
		InternalPostResult(res, tile, estimate_only, only_sending, err_message, my_cmd);
 

	
 
		if (!estimate_only && !only_sending && callback != nullptr) {
 
			std::apply(callback, std::tuple_cat(std::tuple<const CommandCost &, Commands>{ res, Tcmd }, args));
 
		}
 

	
 
		return res.Succeeded();
 
	}
 
};
 

	
 
template <Commands Tcmd>
 
using Command = CommandHelper<Tcmd, typename CommandTraits<Tcmd>::ProcType>;
 

	
 
#endif /* COMMAND_FUNC_H */
src/company_cmd.cpp
Show inline comments
 
@@ -583,49 +583,49 @@ Company *DoStartupNewCompany(bool is_ai,
 

	
 
	return c;
 
}
 

	
 
/** Start the next competitor now. */
 
void StartupCompanies()
 
{
 
	_next_competitor_start = 0;
 
}
 

	
 
/** Start a new competitor company if possible. */
 
static bool MaybeStartNewCompany()
 
{
 
	if (_networking && Company::GetNumItems() >= _settings_client.network.max_companies) return false;
 

	
 
	/* count number of competitors */
 
	uint n = 0;
 
	for (const Company *c : Company::Iterate()) {
 
		if (c->is_ai) n++;
 
	}
 

	
 
	if (n < (uint)_settings_game.difficulty.max_no_competitors) {
 
		/* Send a command to all clients to start up a new AI.
 
		 * Works fine for Multiplayer and Singleplayer */
 
		return DoCommandP(CMD_COMPANY_CTRL, 0, CCA_NEW_AI | INVALID_COMPANY << 16, 0);
 
		return Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | INVALID_COMPANY << 16, 0, {});
 
	}
 

	
 
	return false;
 
}
 

	
 
/** Initialize the pool of companies. */
 
void InitializeCompanies()
 
{
 
	_cur_company_tick_index = 0;
 
}
 

	
 
/**
 
 * May company \a cbig buy company \a csmall?
 
 * @param cbig   Company buying \a csmall.
 
 * @param csmall Company getting bought.
 
 * @return Return \c true if it is allowed.
 
 */
 
bool MayCompanyTakeOver(CompanyID cbig, CompanyID csmall)
 
{
 
	const Company *c1 = Company::Get(cbig);
 
	const Company *c2 = Company::Get(csmall);
 

	
 
	/* Do the combined vehicle counts stay within the limits? */
 
	return c1->group_all[VEH_TRAIN].num_vehicle + c2->group_all[VEH_TRAIN].num_vehicle <= _settings_game.vehicle.max_trains &&
src/company_gui.cpp
Show inline comments
 
@@ -16,48 +16,53 @@
 
#include "viewport_func.h"
 
#include "company_func.h"
 
#include "command_func.h"
 
#include "network/network.h"
 
#include "network/network_gui.h"
 
#include "network/network_func.h"
 
#include "newgrf.h"
 
#include "company_manager_face.h"
 
#include "strings_func.h"
 
#include "date_func.h"
 
#include "widgets/dropdown_type.h"
 
#include "tilehighlight_func.h"
 
#include "company_base.h"
 
#include "core/geometry_func.hpp"
 
#include "object_type.h"
 
#include "rail.h"
 
#include "road.h"
 
#include "engine_base.h"
 
#include "window_func.h"
 
#include "road_func.h"
 
#include "water.h"
 
#include "station_func.h"
 
#include "zoom_func.h"
 
#include "sortlist_type.h"
 
#include "company_cmd.h"
 
#include "economy_cmd.h"
 
#include "group_cmd.h"
 
#include "misc_cmd.h"
 
#include "object_cmd.h"
 

	
 
#include "widgets/company_widget.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
/** Company GUI constants. */
 
static const uint EXP_LINESPACE  = 2;      ///< Amount of vertical space for a horizontal (sub-)total line.
 
static const uint EXP_BLOCKSPACE = 10;     ///< Amount of vertical space between two blocks of numbers.
 

	
 
static void DoSelectCompanyManagerFace(Window *parent);
 
static void ShowCompanyInfrastructure(CompanyID company);
 

	
 
/** Standard unsorted list of expenses. */
 
static ExpensesType _expenses_list_1[] = {
 
	EXPENSES_CONSTRUCTION,
 
	EXPENSES_NEW_VEHICLES,
 
	EXPENSES_TRAIN_RUN,
 
	EXPENSES_ROADVEH_RUN,
 
	EXPENSES_AIRCRAFT_RUN,
 
	EXPENSES_SHIP_RUN,
 
	EXPENSES_PROPERTY,
 
	EXPENSES_TRAIN_INC,
 
	EXPENSES_ROADVEH_INC,
 
@@ -414,53 +419,53 @@ struct CompanyFinancesWindow : Window {
 
			const Company *c = Company::Get(company);
 
			this->SetWidgetDisabledState(WID_CF_INCREASE_LOAN, c->current_loan == _economy.max_loan); // Borrow button only shows when there is any more money to loan.
 
			this->SetWidgetDisabledState(WID_CF_REPAY_LOAN, company != _local_company || c->current_loan == 0); // Repay button only shows when there is any more money to repay.
 
		}
 

	
 
		this->DrawWidgets();
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_CF_TOGGLE_SIZE: // toggle size
 
				this->small = !this->small;
 
				this->SetupWidgets();
 
				if (this->IsShaded()) {
 
					/* Finances window is not resizable, so size hints given during unshading have no effect
 
					 * on the changed appearance of the window. */
 
					this->SetShaded(false);
 
				} else {
 
					this->ReInit();
 
				}
 
				break;
 

	
 
			case WID_CF_INCREASE_LOAN: // increase loan
 
				DoCommandP(CMD_INCREASE_LOAN, STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY, 0, 0, _ctrl_pressed);
 
				Command<CMD_INCREASE_LOAN>::Post(STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY, 0, 0, _ctrl_pressed, {});
 
				break;
 

	
 
			case WID_CF_REPAY_LOAN: // repay loan
 
				DoCommandP(CMD_DECREASE_LOAN, STR_ERROR_CAN_T_REPAY_LOAN, 0, 0, _ctrl_pressed);
 
				Command<CMD_DECREASE_LOAN>::Post(STR_ERROR_CAN_T_REPAY_LOAN, 0, 0, _ctrl_pressed, {});
 
				break;
 

	
 
			case WID_CF_INFRASTRUCTURE: // show infrastructure details
 
				ShowCompanyInfrastructure((CompanyID)this->window_number);
 
				break;
 
		}
 
	}
 

	
 
	void OnHundredthTick() override
 
	{
 
		const Company *c = Company::Get((CompanyID)this->window_number);
 
		if (c->money > CompanyFinancesWindow::max_money) {
 
			CompanyFinancesWindow::max_money = std::max(c->money * 2, CompanyFinancesWindow::max_money * 4);
 
			this->SetupWidgets();
 
			this->ReInit();
 
		}
 
	}
 
};
 

	
 
/** First conservative estimate of the maximum amount of money */
 
Money CompanyFinancesWindow::max_money = INT32_MAX;
 

	
 
static WindowDesc _company_finances_desc(
 
	WDP_AUTO, "company_finances", 0, 0,
 
@@ -974,54 +979,54 @@ public:
 
				}
 
				this->SetDirty();
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnResize() override
 
	{
 
		this->vscroll->SetCapacityFromWidget(this, WID_SCL_MATRIX);
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		bool local = (CompanyID)this->window_number == _local_company;
 
		if (!local) return;
 

	
 
		if (index >= COLOUR_END) index = INVALID_COLOUR;
 

	
 
		if (this->livery_class < LC_GROUP_RAIL) {
 
			/* Set company colour livery */
 
			for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
 
				/* Changed colour for the selected scheme, or all visible schemes if CTRL is pressed. */
 
				if (HasBit(this->sel, scheme) || (_ctrl_pressed && _livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme))) {
 
					DoCommandP(CMD_SET_COMPANY_COLOUR, 0, scheme | (widget == WID_SCL_PRI_COL_DROPDOWN ? 0 : 256), index);
 
					Command<CMD_SET_COMPANY_COLOUR>::Post(0, scheme | (widget == WID_SCL_PRI_COL_DROPDOWN ? 0 : 256), index, {});
 
				}
 
			}
 
		} else {
 
			/* Setting group livery */
 
			DoCommandP(CMD_SET_GROUP_LIVERY, 0, this->sel, (widget == WID_SCL_PRI_COL_DROPDOWN ? 0 : 256) | (index << 16));
 
			Command<CMD_SET_GROUP_LIVERY>::Post(0, this->sel, (widget == WID_SCL_PRI_COL_DROPDOWN ? 0 : 256) | (index << 16), {});
 
		}
 
	}
 

	
 
	/**
 
	 * Some data on this window has become invalid.
 
	 * @param data Information about the changed data.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	void OnInvalidateData(int data = 0, bool gui_scope = true) override
 
	{
 
		if (!gui_scope) return;
 

	
 
		if (data != -1) {
 
			/* data contains a VehicleType, rebuild list if it displayed */
 
			if (this->livery_class == data + LC_GROUP_RAIL) {
 
				this->groups.ForceRebuild();
 
				this->BuildGroupList((CompanyID)this->window_number);
 
				this->SetRows();
 

	
 
				if (!Group::IsValidID(this->sel)) {
 
					this->sel = INVALID_GROUP;
 
					if (this->groups.size() > 0) this->sel = this->groups[0]->index;
 
				}
 

	
 
@@ -1560,49 +1565,49 @@ public:
 

	
 
			case WID_SCMF_COLLAR:
 
				this->DrawFaceStringLabel(WID_SCMF_COLLAR,      GetCompanyManagerFaceBits(this->face, CMFV_COLLAR,      this->ge), false);
 
				break;
 

	
 
			case WID_SCMF_FACE:
 
				DrawCompanyManagerFace(this->face, Company::Get((CompanyID)this->window_number)->colour, r.left, r.top);
 
				break;
 
		}
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			/* Toggle size, advanced/simple face selection */
 
			case WID_SCMF_TOGGLE_LARGE_SMALL:
 
			case WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON:
 
				this->advanced = !this->advanced;
 
				this->SelectDisplayPlanes(this->advanced);
 
				this->ReInit();
 
				break;
 

	
 
			/* OK button */
 
			case WID_SCMF_ACCEPT:
 
				DoCommandP(CMD_SET_COMPANY_MANAGER_FACE, 0, 0, this->face);
 
				Command<CMD_SET_COMPANY_MANAGER_FACE>::Post(0, 0, this->face, {});
 
				FALLTHROUGH;
 

	
 
			/* Cancel button */
 
			case WID_SCMF_CANCEL:
 
				this->Close();
 
				break;
 

	
 
			/* Load button */
 
			case WID_SCMF_LOAD:
 
				this->face = _company_manager_face;
 
				ScaleAllCompanyManagerFaceBits(this->face);
 
				ShowErrorMessage(STR_FACE_LOAD_DONE, INVALID_STRING_ID, WL_INFO);
 
				this->UpdateData();
 
				this->SetDirty();
 
				break;
 

	
 
			/* 'Company manager face number' button, view and/or set company manager face number */
 
			case WID_SCMF_FACECODE:
 
				SetDParam(0, this->face);
 
				ShowQueryString(STR_JUST_INT, STR_FACE_FACECODE_CAPTION, 10 + 1, this, CS_NUMERAL, QSF_NONE);
 
				break;
 

	
 
			/* Save button */
 
			case WID_SCMF_SAVE:
 
@@ -2555,117 +2560,117 @@ struct CompanyWindow : Window
 
				break;
 

	
 
			case WID_C_RELOCATE_HQ:
 
				if (this->IsWidgetLowered(WID_C_RELOCATE_HQ)) {
 
					ResetObjectToPlace();
 
					this->RaiseButtons();
 
					break;
 
				}
 
				SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, HT_RECT, this);
 
				SetTileSelectSize(2, 2);
 
				this->LowerWidget(WID_C_RELOCATE_HQ);
 
				this->SetWidgetDirty(WID_C_RELOCATE_HQ);
 
				break;
 

	
 
			case WID_C_VIEW_INFRASTRUCTURE:
 
				ShowCompanyInfrastructure((CompanyID)this->window_number);
 
				break;
 

	
 
			case WID_C_GIVE_MONEY:
 
				this->query_widget = WID_C_GIVE_MONEY;
 
				ShowQueryString(STR_EMPTY, STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION, 30, this, CS_NUMERAL, QSF_NONE);
 
				break;
 

	
 
			case WID_C_BUY_SHARE:
 
				DoCommandP(CMD_BUY_SHARE_IN_COMPANY, STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS, (TileIndex)0, this->window_number, 0);
 
				Command<CMD_BUY_SHARE_IN_COMPANY>::Post(STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS, 0, this->window_number, 0, {});
 
				break;
 

	
 
			case WID_C_SELL_SHARE:
 
				DoCommandP(CMD_SELL_SHARE_IN_COMPANY, STR_ERROR_CAN_T_SELL_25_SHARE_IN, (TileIndex)0, this->window_number, 0);
 
				Command<CMD_SELL_SHARE_IN_COMPANY>::Post(STR_ERROR_CAN_T_SELL_25_SHARE_IN, 0, this->window_number, 0, {});
 
				break;
 

	
 
			case WID_C_COMPANY_PASSWORD:
 
				if (this->window_number == _local_company) ShowNetworkCompanyPasswordWindow(this);
 
				break;
 

	
 
			case WID_C_COMPANY_JOIN: {
 
				this->query_widget = WID_C_COMPANY_JOIN;
 
				CompanyID company = (CompanyID)this->window_number;
 
				if (_network_server) {
 
					NetworkServerDoMove(CLIENT_ID_SERVER, company);
 
					MarkWholeScreenDirty();
 
				} else if (NetworkCompanyIsPassworded(company)) {
 
					/* ask for the password */
 
					ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, this, CS_ALPHANUMERAL, QSF_PASSWORD);
 
				} else {
 
					/* just send the join command */
 
					NetworkClientRequestMove(company);
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnHundredthTick() override
 
	{
 
		/* redraw the window every now and then */
 
		this->SetDirty();
 
	}
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		if (DoCommandP(CMD_BUILD_OBJECT, STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS, tile, OBJECT_HQ, 0) && !_shift_pressed) {
 
		if (Command<CMD_BUILD_OBJECT>::Post(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS, tile, OBJECT_HQ, 0, {}) && !_shift_pressed) {
 
			ResetObjectToPlace();
 
			this->RaiseButtons();
 
		}
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		this->RaiseButtons();
 
	}
 

	
 
	void OnQueryTextFinished(char *str) override
 
	{
 
		if (str == nullptr) return;
 

	
 
		switch (this->query_widget) {
 
			default: NOT_REACHED();
 

	
 
			case WID_C_GIVE_MONEY: {
 
				Money money = (Money)(strtoull(str, nullptr, 10) / _currency->rate);
 
				uint32 money_c = Clamp(ClampToI32(money), 0, 20000000); // Clamp between 20 million and 0
 

	
 
				DoCommandP(CMD_GIVE_MONEY, STR_ERROR_CAN_T_GIVE_MONEY, 0, money_c, this->window_number);
 
				Command<CMD_GIVE_MONEY>::Post(STR_ERROR_CAN_T_GIVE_MONEY, 0, money_c, this->window_number, {});
 
				break;
 
			}
 

	
 
			case WID_C_PRESIDENT_NAME:
 
				DoCommandP(CMD_RENAME_PRESIDENT, STR_ERROR_CAN_T_CHANGE_PRESIDENT, 0, 0, 0, str);
 
				Command<CMD_RENAME_PRESIDENT>::Post(STR_ERROR_CAN_T_CHANGE_PRESIDENT, 0, 0, 0, str);
 
				break;
 

	
 
			case WID_C_COMPANY_NAME:
 
				DoCommandP(CMD_RENAME_COMPANY, STR_ERROR_CAN_T_CHANGE_COMPANY_NAME, 0, 0, 0, str);
 
				Command<CMD_RENAME_COMPANY>::Post(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME, 0, 0, 0, str);
 
				break;
 

	
 
			case WID_C_COMPANY_JOIN:
 
				NetworkClientRequestMove((CompanyID)this->window_number, str);
 
				break;
 
		}
 
	}
 

	
 

	
 
	/**
 
	 * Some data on this window has become invalid.
 
	 * @param data Information about the changed data.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	void OnInvalidateData(int data = 0, bool gui_scope = true) override
 
	{
 
		if (this->window_number == _local_company) return;
 

	
 
		if (_settings_game.economy.allow_shares) { // Shares are allowed
 
			const Company *c = Company::Get(this->window_number);
 

	
 
			/* If all shares are owned by someone (none by nobody), disable buy button */
 
			this->SetWidgetDisabledState(WID_C_BUY_SHARE, GetAmountOwnedBy(c, INVALID_OWNER) == 0 ||
 
					/* Only 25% left to buy. If the company is human, disable buying it up.. TODO issues! */
 
@@ -2750,49 +2755,49 @@ struct BuyCompanyWindow : Window {
 
			case WID_BC_FACE: {
 
				const Company *c = Company::Get((CompanyID)this->window_number);
 
				DrawCompanyManagerFace(c->face, c->colour, r.left, r.top);
 
				break;
 
			}
 

	
 
			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);
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_BC_NO:
 
				this->Close();
 
				break;
 

	
 
			case WID_BC_YES:
 
				DoCommandP(CMD_BUY_COMPANY, STR_ERROR_CAN_T_BUY_COMPANY, (TileIndex)0, this->window_number, 0);
 
				Command<CMD_BUY_COMPANY>::Post(STR_ERROR_CAN_T_BUY_COMPANY, 0, this->window_number, 0, {});
 
				break;
 
		}
 
	}
 
};
 

	
 
static const NWidgetPart _nested_buy_company_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
 
		NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE, WID_BC_CAPTION), SetDataTip(STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
	EndContainer(),
 
	NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE),
 
		NWidget(NWID_VERTICAL), SetPIP(8, 8, 8),
 
			NWidget(NWID_HORIZONTAL), SetPIP(8, 10, 8),
 
				NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BC_FACE), SetFill(0, 1),
 
				NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BC_QUESTION), SetMinimalSize(240, 0), SetFill(1, 1),
 
			EndContainer(),
 
			NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(100, 10, 100),
 
				NWidget(WWT_TEXTBTN, COLOUR_LIGHT_BLUE, WID_BC_NO), SetMinimalSize(60, 12), SetDataTip(STR_QUIT_NO, STR_NULL), SetFill(1, 0),
 
				NWidget(WWT_TEXTBTN, COLOUR_LIGHT_BLUE, WID_BC_YES), SetMinimalSize(60, 12), SetDataTip(STR_QUIT_YES, STR_NULL), SetFill(1, 0),
 
			EndContainer(),
 
		EndContainer(),
 
	EndContainer(),
 
};
 

	
src/console_cmds.cpp
Show inline comments
 
@@ -21,48 +21,50 @@
 
#include "network/network_client.h"
 
#include "command_func.h"
 
#include "settings_func.h"
 
#include "fios.h"
 
#include "fileio_func.h"
 
#include "screenshot.h"
 
#include "genworld.h"
 
#include "strings_func.h"
 
#include "viewport_func.h"
 
#include "window_func.h"
 
#include "date_func.h"
 
#include "company_func.h"
 
#include "gamelog.h"
 
#include "ai/ai.hpp"
 
#include "ai/ai_config.hpp"
 
#include "newgrf.h"
 
#include "newgrf_profiling.h"
 
#include "console_func.h"
 
#include "engine_base.h"
 
#include "road.h"
 
#include "rail.h"
 
#include "game/game.hpp"
 
#include "table/strings.h"
 
#include "walltime_func.h"
 
#include "company_cmd.h"
 
#include "misc_cmd.h"
 

	
 
#include "safeguards.h"
 

	
 
/* scriptfile handling */
 
static uint _script_current_depth; ///< Depth of scripts running (used to abort execution when #ConReturn is encountered).
 

	
 
/** File list storage for the console, for caching the last 'ls' command. */
 
class ConsoleFileList : public FileList {
 
public:
 
	ConsoleFileList() : FileList()
 
	{
 
		this->file_list_valid = false;
 
	}
 

	
 
	/** Declare the file storage cache as being invalid, also clears all stored files. */
 
	void InvalidateFileList()
 
	{
 
		this->clear();
 
		this->file_list_valid = false;
 
	}
 

	
 
	/**
 
	 * (Re-)validate the file storage cache. Only makes a change if the storage was invalid, or if \a force_reload.
 
	 * @param force_reload Always reload the file storage cache.
 
@@ -609,71 +611,71 @@ DEF_CONSOLE_CMD(ConBanList)
 
	IConsolePrint(CC_DEFAULT, "Banlist:");
 

	
 
	uint i = 1;
 
	for (const auto &entry : _network_ban_list) {
 
		IConsolePrint(CC_DEFAULT, "  {}) {}", i, entry);
 
		i++;
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConPauseGame)
 
{
 
	if (argc == 0) {
 
		IConsolePrint(CC_HELP, "Pause a network game. Usage: 'pause'.");
 
		return true;
 
	}
 

	
 
	if (_game_mode == GM_MENU) {
 
		IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
 
		return true;
 
	}
 

	
 
	if ((_pause_mode & PM_PAUSED_NORMAL) == PM_UNPAUSED) {
 
		DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 1);
 
		Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 1, {});
 
		if (!_networking) IConsolePrint(CC_DEFAULT, "Game paused.");
 
	} else {
 
		IConsolePrint(CC_DEFAULT, "Game is already paused.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConUnpauseGame)
 
{
 
	if (argc == 0) {
 
		IConsolePrint(CC_HELP, "Unpause a network game. Usage: 'unpause'.");
 
		return true;
 
	}
 

	
 
	if (_game_mode == GM_MENU) {
 
		IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
 
		return true;
 
	}
 

	
 
	if ((_pause_mode & PM_PAUSED_NORMAL) != PM_UNPAUSED) {
 
		DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 0);
 
		Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 0, {});
 
		if (!_networking) IConsolePrint(CC_DEFAULT, "Game unpaused.");
 
	} else if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED) {
 
		IConsolePrint(CC_DEFAULT, "Game is in error state and cannot be unpaused via console.");
 
	} else if (_pause_mode != PM_UNPAUSED) {
 
		IConsolePrint(CC_DEFAULT, "Game cannot be unpaused manually; disable pause_on_join/min_active_clients.");
 
	} else {
 
		IConsolePrint(CC_DEFAULT, "Game is already unpaused.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConRcon)
 
{
 
	if (argc == 0) {
 
		IConsolePrint(CC_HELP, "Remote control the server from another client. Usage: 'rcon <password> <command>'.");
 
		IConsolePrint(CC_HELP, "Remember to enclose the command in quotes, otherwise only the first parameter is sent.");
 
		return true;
 
	}
 

	
 
	if (argc < 3) return false;
 

	
 
	if (_network_server) {
 
		IConsoleCmdExec(argv[2]);
 
@@ -842,49 +844,49 @@ DEF_CONSOLE_CMD(ConResetCompany)
 
	CompanyID index = (CompanyID)(atoi(argv[1]) - 1);
 

	
 
	/* Check valid range */
 
	if (!Company::IsValidID(index)) {
 
		IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
 
		return true;
 
	}
 

	
 
	if (!Company::IsHumanID(index)) {
 
		IConsolePrint(CC_ERROR, "Company is owned by an AI.");
 
		return true;
 
	}
 

	
 
	if (NetworkCompanyHasClients(index)) {
 
		IConsolePrint(CC_ERROR, "Cannot remove company: a client is connected to that company.");
 
		return false;
 
	}
 
	const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER);
 
	if (ci->client_playas == index) {
 
		IConsolePrint(CC_ERROR, "Cannot remove company: the server is connected to that company.");
 
		return true;
 
	}
 

	
 
	/* It is safe to remove this company */
 
	DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | index << 16 | CRR_MANUAL << 24, 0);
 
	Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | index << 16 | CRR_MANUAL << 24, 0, {});
 
	IConsolePrint(CC_DEFAULT, "Company deleted.");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConNetworkClients)
 
{
 
	if (argc == 0) {
 
		IConsolePrint(CC_HELP, "Get a list of connected clients including their ID, name, company-id, and IP. Usage: 'clients'.");
 
		return true;
 
	}
 

	
 
	NetworkPrintClients();
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConNetworkReconnect)
 
{
 
	if (argc == 0) {
 
		IConsolePrint(CC_HELP, "Reconnect to server to which you were connected last time. Usage: 'reconnect [<company>]'.");
 
		IConsolePrint(CC_HELP, "Company 255 is spectator (default, if not specified), 0 means creating new company.");
 
		IConsolePrint(CC_HELP, "All others are a certain company with Company 1 being #1.");
 
		return true;
 
@@ -1199,123 +1201,123 @@ DEF_CONSOLE_CMD(ConStartAI)
 
		 * the version the user wants to load. */
 
		if (!config->HasScript()) {
 
			char *name = stredup(argv[1]);
 
			char *e = strrchr(name, '.');
 
			if (e != nullptr) {
 
				*e = '\0';
 
				e++;
 

	
 
				int version = atoi(e);
 
				config->Change(name, version, true);
 
			}
 
			free(name);
 
		}
 

	
 
		if (!config->HasScript()) {
 
			IConsolePrint(CC_ERROR, "Failed to load the specified AI.");
 
			return true;
 
		}
 
		if (argc == 3) {
 
			config->StringToSettings(argv[2]);
 
		}
 
	}
 

	
 
	/* Start a new AI company */
 
	DoCommandP(CMD_COMPANY_CTRL, 0, CCA_NEW_AI | INVALID_COMPANY << 16, 0);
 
	Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | INVALID_COMPANY << 16, 0, {});
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConReloadAI)
 
{
 
	if (argc != 2) {
 
		IConsolePrint(CC_HELP, "Reload an AI. Usage: 'reload_ai <company-id>'.");
 
		IConsolePrint(CC_HELP, "Reload the AI with the given company id. For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
 
		return true;
 
	}
 

	
 
	if (_game_mode != GM_NORMAL) {
 
		IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
 
		return true;
 
	}
 

	
 
	if (_networking && !_network_server) {
 
		IConsolePrint(CC_ERROR, "Only the server can reload an AI.");
 
		return true;
 
	}
 

	
 
	CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
 
	if (!Company::IsValidID(company_id)) {
 
		IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
 
		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) || company_id == _local_company) {
 
		IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
 
		return true;
 
	}
 

	
 
	/* First kill the company of the AI, then start a new one. This should start the current AI again */
 
	DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0);
 
	DoCommandP(CMD_COMPANY_CTRL, 0, CCA_NEW_AI | company_id << 16, 0);
 
	Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, {});
 
	Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | company_id << 16, 0, {});
 
	IConsolePrint(CC_DEFAULT, "AI reloaded.");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConStopAI)
 
{
 
	if (argc != 2) {
 
		IConsolePrint(CC_HELP, "Stop an AI. Usage: 'stop_ai <company-id>'.");
 
		IConsolePrint(CC_HELP, "Stop the AI with the given company id. For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
 
		return true;
 
	}
 

	
 
	if (_game_mode != GM_NORMAL) {
 
		IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
 
		return true;
 
	}
 

	
 
	if (_networking && !_network_server) {
 
		IConsolePrint(CC_ERROR, "Only the server can stop an AI.");
 
		return true;
 
	}
 

	
 
	CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
 
	if (!Company::IsValidID(company_id)) {
 
		IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
 
		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) || company_id == _local_company) {
 
		IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
 
		return true;
 
	}
 

	
 
	/* Now kill the company of the AI. */
 
	DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0);
 
	Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, {});
 
	IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConRescanAI)
 
{
 
	if (argc == 0) {
 
		IConsolePrint(CC_HELP, "Rescan the AI dir for scripts. Usage: 'rescan_ai'.");
 
		return true;
 
	}
 

	
 
	if (_networking && !_network_server) {
 
		IConsolePrint(CC_ERROR, "Only the server can rescan the AI dir for scripts.");
 
		return true;
 
	}
 

	
 
	AI::Rescan();
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConRescanGame)
 
{
src/depot_gui.cpp
Show inline comments
 
@@ -5,48 +5,51 @@
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file depot_gui.cpp The GUI for depots. */
 

	
 
#include "stdafx.h"
 
#include "train.h"
 
#include "roadveh.h"
 
#include "ship.h"
 
#include "aircraft.h"
 
#include "gui.h"
 
#include "textbuf_gui.h"
 
#include "viewport_func.h"
 
#include "command_func.h"
 
#include "depot_base.h"
 
#include "spritecache.h"
 
#include "strings_func.h"
 
#include "vehicle_func.h"
 
#include "company_func.h"
 
#include "tilehighlight_func.h"
 
#include "window_gui.h"
 
#include "vehiclelist.h"
 
#include "order_backup.h"
 
#include "zoom_func.h"
 
#include "depot_cmd.h"
 
#include "train_cmd.h"
 
#include "vehicle_cmd.h"
 

	
 
#include "widgets/depot_widget.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/*
 
 * Since all depot window sizes aren't the same, we need to modify sizes a little.
 
 * It's done with the following arrays of widget indexes. Each of them tells if a widget side should be moved and in what direction.
 
 * How long they should be moved and for what window types are controlled in ShowDepotWindow()
 
 */
 

	
 
/** Nested widget definition for train depots. */
 
static const NWidgetPart _nested_train_depot_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_GREY),
 
		NWidget(NWID_SELECTION, INVALID_COLOUR, WID_D_SHOW_RENAME), // rename button
 
			NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_RENAME), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_DEPOT_RENAME_TOOLTIP),
 
		EndContainer(),
 
		NWidget(WWT_CAPTION, COLOUR_GREY, WID_D_CAPTION), SetDataTip(STR_DEPOT_CAPTION, STR_NULL),
 
		NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_LOCATION), SetMinimalSize(12, 14), SetDataTip(SPR_GOTO_LOCATION, STR_NULL),
 
		NWidget(WWT_SHADEBOX, COLOUR_GREY),
 
		NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
 
@@ -121,49 +124,49 @@ extern void DepotSortList(VehicleList *l
 
void CcCloneVehicle(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Failed()) return;
 

	
 
	const Vehicle *v = Vehicle::Get(_new_vehicle_id);
 

	
 
	ShowVehicleViewWindow(v);
 
}
 

	
 
static void TrainDepotMoveVehicle(const Vehicle *wagon, VehicleID sel, const Vehicle *head)
 
{
 
	const Vehicle *v = Vehicle::Get(sel);
 

	
 
	if (v == wagon) return;
 

	
 
	if (wagon == nullptr) {
 
		if (head != nullptr) wagon = head->Last();
 
	} else {
 
		wagon = wagon->Previous();
 
		if (wagon == nullptr) return;
 
	}
 

	
 
	if (wagon == v) return;
 

	
 
	DoCommandP(CMD_MOVE_RAIL_VEHICLE, STR_ERROR_CAN_T_MOVE_VEHICLE, v->tile, v->index | (_ctrl_pressed ? 1 : 0) << 20, wagon == nullptr ? INVALID_VEHICLE : wagon->index);
 
	Command<CMD_MOVE_RAIL_VEHICLE>::Post(STR_ERROR_CAN_T_MOVE_VEHICLE, v->tile, v->index | (_ctrl_pressed ? 1 : 0) << 20, wagon == nullptr ? INVALID_VEHICLE : wagon->index, {});
 
}
 

	
 
static VehicleCellSize _base_block_sizes_depot[VEH_COMPANY_END];    ///< Cell size for vehicle images in the depot view.
 
static VehicleCellSize _base_block_sizes_purchase[VEH_COMPANY_END]; ///< Cell size for vehicle images in the purchase list.
 
static uint _consistent_train_width;                                ///< Whether trains of all lengths are consistently scaled. Either TRAININFO_DEFAULT_VEHICLE_WIDTH, VEHICLEINFO_FULL_VEHICLE_WIDTH, or 0.
 

	
 
/**
 
 * Get the GUI cell size for a vehicle image.
 
 * @param type Vehicle type to get the size for.
 
 * @param image_type Image type to get size for.
 
 * @pre image_type == EIT_IN_DEPOT || image_type == EIT_PURCHASE
 
 * @return Cell dimensions for the vehicle and image type.
 
 */
 
VehicleCellSize GetVehicleImageCellSize(VehicleType type, EngineImageType image_type)
 
{
 
	switch (image_type) {
 
		case EIT_IN_DEPOT: return _base_block_sizes_depot[type];
 
		case EIT_PURCHASE: return _base_block_sizes_purchase[type];
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
static void InitBlocksizeForVehicles(VehicleType type, EngineImageType image_type)
 
{
 
@@ -782,83 +785,83 @@ struct DepotWindow : Window {
 

	
 
					SetObjectToPlaceWnd(clone_icons[this->type], PAL_NONE, HT_VEHICLE, this);
 
				} else {
 
					ResetObjectToPlace();
 
				}
 
				break;
 

	
 
			case WID_D_LOCATION:
 
				if (_ctrl_pressed) {
 
					ShowExtraViewportWindow(this->window_number);
 
				} else {
 
					ScrollMainWindowToTile(this->window_number);
 
				}
 
				break;
 

	
 
			case WID_D_RENAME: // Rename button
 
				SetDParam(0, this->type);
 
				SetDParam(1, Depot::GetByTile((TileIndex)this->window_number)->index);
 
				ShowQueryString(STR_DEPOT_NAME, STR_DEPOT_RENAME_DEPOT_CAPTION, MAX_LENGTH_DEPOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
 
				break;
 

	
 
			case WID_D_STOP_ALL:
 
			case WID_D_START_ALL: {
 
				VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner);
 
				DoCommandP(CMD_MASS_START_STOP, this->window_number, (widget == WID_D_START_ALL ? (1 << 0) : 0), vli.Pack());
 
				Command<CMD_MASS_START_STOP>::Post(this->window_number, (widget == WID_D_START_ALL ? (1 << 0) : 0), vli.Pack(), {});
 
				break;
 
			}
 

	
 
			case WID_D_SELL_ALL:
 
				/* Only open the confirmation window if there are anything to sell */
 
				if (this->vehicle_list.size() != 0 || this->wagon_list.size() != 0) {
 
					SetDParam(0, this->type);
 
					SetDParam(1, this->GetDepotIndex());
 
					ShowQuery(
 
						STR_DEPOT_CAPTION,
 
						STR_DEPOT_SELL_CONFIRMATION_TEXT,
 
						this,
 
						DepotSellAllConfirmationCallback
 
					);
 
				}
 
				break;
 

	
 
			case WID_D_VEHICLE_LIST:
 
				ShowVehicleListWindow(GetTileOwner(this->window_number), this->type, (TileIndex)this->window_number);
 
				break;
 

	
 
			case WID_D_AUTOREPLACE:
 
				DoCommandP(CMD_DEPOT_MASS_AUTOREPLACE, this->window_number, this->type, 0);
 
				Command<CMD_DEPOT_MASS_AUTOREPLACE>::Post(this->window_number, this->type, 0, {});
 
				break;
 

	
 
		}
 
	}
 

	
 
	void OnQueryTextFinished(char *str) override
 
	{
 
		if (str == nullptr) return;
 

	
 
		/* Do depot renaming */
 
		DoCommandP(CMD_RENAME_DEPOT, STR_ERROR_CAN_T_RENAME_DEPOT, 0, this->GetDepotIndex(), 0, str);
 
		Command<CMD_RENAME_DEPOT>::Post(STR_ERROR_CAN_T_RENAME_DEPOT, 0, this->GetDepotIndex(), 0, str);
 
	}
 

	
 
	bool OnRightClick(Point pt, int widget) override
 
	{
 
		if (widget != WID_D_MATRIX) return false;
 

	
 
		GetDepotVehiclePtData gdvp = { nullptr, nullptr };
 
		const Vehicle *v = nullptr;
 
		NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_D_MATRIX);
 
		DepotGUIAction mode = this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, &gdvp);
 

	
 
		if (this->type == VEH_TRAIN) v = gdvp.wagon;
 

	
 
		if (v == nullptr || mode != MODE_DRAG_VEHICLE) return false;
 

	
 
		CargoArray capacity, loaded;
 

	
 
		/* Display info for single (articulated) vehicle, or for whole chain starting with selected vehicle */
 
		bool whole_chain = (this->type == VEH_TRAIN && _ctrl_pressed);
 

	
 
		/* loop through vehicle chain and collect cargoes */
 
		uint num = 0;
 
		for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
 
			if (w->cargo_cap > 0 && w->cargo_type < NUM_CARGO) {
 
@@ -884,52 +887,52 @@ struct DepotWindow : Window {
 
			SetDParam(1, loaded[cargo_type]);   // {CARGO} #2
 
			SetDParam(2, cargo_type);           // {SHORTCARGO} #1
 
			SetDParam(3, capacity[cargo_type]); // {SHORTCARGO} #2
 
			pos = GetString(pos, STR_DEPOT_VEHICLE_TOOLTIP_CARGO, lastof(details));
 
		}
 

	
 
		/* Show tooltip window */
 
		uint64 args[2];
 
		args[0] = (whole_chain ? num : v->engine_type);
 
		args[1] = (uint64)(size_t)details;
 
		GuiShowTooltips(this, whole_chain ? STR_DEPOT_VEHICLE_TOOLTIP_CHAIN : STR_DEPOT_VEHICLE_TOOLTIP, 2, args, TCC_RIGHT_CLICK);
 

	
 
		return true;
 
	}
 

	
 
	/**
 
	 * Clones a vehicle
 
	 * @param v the original vehicle to clone
 
	 * @return Always true.
 
	 */
 
	bool OnVehicleSelect(const Vehicle *v) override
 
	{
 
		if (_ctrl_pressed) {
 
			/* Share-clone, do not open new viewport, and keep tool active */
 
			DoCommandP(CMD_CLONE_VEHICLE, STR_ERROR_CAN_T_BUY_TRAIN + v->type, this->window_number, v->index, 1);
 
			Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, this->window_number, v->index, 1, {});
 
		} else {
 
			/* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to changs things on new vehicle) */
 
			if (DoCommandP(CMD_CLONE_VEHICLE, STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, this->window_number, v->index, 0)) {
 
			/* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to change things on new vehicle) */
 
			if (Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, this->window_number, v->index, 0, {})) {
 
				ResetObjectToPlace();
 
			}
 
		}
 

	
 
		return true;
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		/* abort clone */
 
		this->RaiseWidget(WID_D_CLONE);
 
		this->SetWidgetDirty(WID_D_CLONE);
 

	
 
		/* abort drag & drop */
 
		this->sel = INVALID_VEHICLE;
 
		this->vehicle_over = INVALID_VEHICLE;
 
		this->SetWidgetDirty(WID_D_MATRIX);
 

	
 
		if (this->hovered_widget != -1) {
 
			this->SetWidgetLoweredState(this->hovered_widget, false);
 
			this->SetWidgetDirty(this->hovered_widget);
 
			this->hovered_widget = -1;
 
		}
 
	}
 
@@ -981,74 +984,74 @@ struct DepotWindow : Window {
 
		}
 

	
 
		if (this->vehicle_over == new_vehicle_over) return;
 

	
 
		this->vehicle_over = new_vehicle_over;
 
		this->SetWidgetDirty(widget);
 
	}
 

	
 
	void OnDragDrop(Point pt, int widget) override
 
	{
 
		switch (widget) {
 
			case WID_D_MATRIX: {
 
				const Vehicle *v = nullptr;
 
				VehicleID sel = this->sel;
 

	
 
				this->sel = INVALID_VEHICLE;
 
				this->SetDirty();
 

	
 
				NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_D_MATRIX);
 
				if (this->type == VEH_TRAIN) {
 
					GetDepotVehiclePtData gdvp = { nullptr, nullptr };
 

	
 
					if (this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, &gdvp) == MODE_DRAG_VEHICLE && sel != INVALID_VEHICLE) {
 
						if (gdvp.wagon != nullptr && gdvp.wagon->index == sel && _ctrl_pressed) {
 
							DoCommandP(CMD_REVERSE_TRAIN_DIRECTION, STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE, Vehicle::Get(sel)->tile, Vehicle::Get(sel)->index, true);
 
							Command<CMD_REVERSE_TRAIN_DIRECTION>::Post(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE, Vehicle::Get(sel)->tile, Vehicle::Get(sel)->index, true, {});
 
						} else if (gdvp.wagon == nullptr || gdvp.wagon->index != sel) {
 
							this->vehicle_over = INVALID_VEHICLE;
 
							TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head);
 
						} else if (gdvp.head != nullptr && gdvp.head->IsFrontEngine()) {
 
							ShowVehicleViewWindow(gdvp.head);
 
						}
 
					}
 
				} else if (this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, nullptr) == MODE_DRAG_VEHICLE && v != nullptr && sel == v->index) {
 
					ShowVehicleViewWindow(v);
 
				}
 
				break;
 
			}
 

	
 
			case WID_D_SELL: case WID_D_SELL_CHAIN: {
 
				if (this->IsWidgetDisabled(widget)) return;
 
				if (this->sel == INVALID_VEHICLE) return;
 

	
 
				this->HandleButtonClick(widget);
 

	
 
				const Vehicle *v = Vehicle::Get(this->sel);
 
				this->sel = INVALID_VEHICLE;
 
				this->SetDirty();
 

	
 
				int sell_cmd = (v->type == VEH_TRAIN && (widget == WID_D_SELL_CHAIN || _ctrl_pressed)) ? 1 : 0;
 
				DoCommandP(CMD_SELL_VEHICLE, GetCmdSellVehMsg(v->type), v->tile, v->index | sell_cmd << 20 | MAKE_ORDER_BACKUP_FLAG, 0);
 
				Command<CMD_SELL_VEHICLE>::Post(GetCmdSellVehMsg(v->type), v->tile, v->index | sell_cmd << 20 | MAKE_ORDER_BACKUP_FLAG, 0, {});
 
				break;
 
			}
 

	
 
			default:
 
				this->sel = INVALID_VEHICLE;
 
				this->SetDirty();
 
				break;
 
		}
 
		this->hovered_widget = -1;
 
		_cursor.vehchain = false;
 
	}
 

	
 
	void OnTimeout() override
 
	{
 
		if (!this->IsWidgetDisabled(WID_D_SELL)) {
 
			this->RaiseWidget(WID_D_SELL);
 
			this->SetWidgetDirty(WID_D_SELL);
 
		}
 
		if (this->GetWidget<NWidgetBase>(WID_D_SELL) != nullptr && !this->IsWidgetDisabled(WID_D_SELL_CHAIN)) {
 
			this->RaiseWidget(WID_D_SELL_CHAIN);
 
			this->SetWidgetDirty(WID_D_SELL_CHAIN);
 
		}
 
	}
 

	
 
@@ -1070,49 +1073,49 @@ struct DepotWindow : Window {
 
			this->SetWidgetDirty(WID_D_MATRIX);
 
			return ES_HANDLED;
 
		}
 

	
 
		return ES_NOT_HANDLED;
 
	}
 

	
 
	/**
 
	 * Gets the DepotID of the current window.
 
	 * In the case of airports, this is the station ID.
 
	 * @return Depot or station ID of this window.
 
	 */
 
	inline uint16 GetDepotIndex() const
 
	{
 
		return (this->type == VEH_AIRCRAFT) ? ::GetStationIndex(this->window_number) : ::GetDepotIndex(this->window_number);
 
	}
 
};
 

	
 
static void DepotSellAllConfirmationCallback(Window *win, bool confirmed)
 
{
 
	if (confirmed) {
 
		DepotWindow *w = (DepotWindow*)win;
 
		TileIndex tile = w->window_number;
 
		byte vehtype = w->type;
 
		DoCommandP(CMD_DEPOT_SELL_ALL_VEHICLES, tile, vehtype, 0);
 
		Command<CMD_DEPOT_SELL_ALL_VEHICLES>::Post(tile, vehtype, 0, {});
 
	}
 
}
 

	
 
/**
 
 * Opens a depot window
 
 * @param tile The tile where the depot/hangar is located
 
 * @param type The type of vehicles in the depot
 
 */
 
void ShowDepotWindow(TileIndex tile, VehicleType type)
 
{
 
	if (BringWindowToFrontById(WC_VEHICLE_DEPOT, tile) != nullptr) return;
 

	
 
	WindowDesc *desc;
 
	switch (type) {
 
		default: NOT_REACHED();
 
		case VEH_TRAIN:    desc = &_train_depot_desc;    break;
 
		case VEH_ROAD:     desc = &_road_depot_desc;     break;
 
		case VEH_SHIP:     desc = &_ship_depot_desc;     break;
 
		case VEH_AIRCRAFT: desc = &_aircraft_depot_desc; break;
 
	}
 

	
 
	new DepotWindow(desc, tile, type);
 
}
 

	
src/dock_gui.cpp
Show inline comments
 
@@ -7,48 +7,50 @@
 

	
 
/** @file dock_gui.cpp GUI to create amazing water objects. */
 

	
 
#include "stdafx.h"
 
#include "terraform_gui.h"
 
#include "window_gui.h"
 
#include "station_gui.h"
 
#include "command_func.h"
 
#include "water.h"
 
#include "window_func.h"
 
#include "vehicle_func.h"
 
#include "sound_func.h"
 
#include "viewport_func.h"
 
#include "gfx_func.h"
 
#include "company_func.h"
 
#include "slope_func.h"
 
#include "tilehighlight_func.h"
 
#include "company_base.h"
 
#include "hotkeys.h"
 
#include "gui.h"
 
#include "zoom_func.h"
 
#include "tunnelbridge_cmd.h"
 
#include "dock_cmd.h"
 
#include "station_cmd.h"
 
#include "water_cmd.h"
 
#include "waypoint_cmd.h"
 

	
 
#include "widgets/dock_widget.h"
 

	
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
static void ShowBuildDockStationPicker(Window *parent);
 
static void ShowBuildDocksDepotPicker(Window *parent);
 

	
 
static Axis _ship_depot_direction;
 

	
 
void CcBuildDocks(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Failed()) return;
 

	
 
	if (_settings_client.sound.confirm) SndPlayTileFx(SND_02_CONSTRUCTION_WATER, tile);
 
	if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
}
 

	
 
void CcPlaySound_CONSTRUCTION_WATER(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_02_CONSTRUCTION_WATER, tile);
 
@@ -173,115 +175,115 @@ struct BuildDocksToolbarWindow : Window 
 
				break;
 

	
 
			case WID_DT_RIVER: // Build river button (in scenario editor)
 
				if (_game_mode != GM_EDITOR) return;
 
				HandlePlacePushButton(this, WID_DT_RIVER, SPR_CURSOR_RIVER, HT_RECT | HT_DIAGONAL);
 
				break;
 

	
 
			case WID_DT_BUILD_AQUEDUCT: // Build aqueduct button
 
				HandlePlacePushButton(this, WID_DT_BUILD_AQUEDUCT, SPR_CURSOR_AQUEDUCT, HT_SPECIAL);
 
				break;
 

	
 
			default: return;
 
		}
 
		this->last_clicked_widget = (DockToolbarWidgets)widget;
 
	}
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		switch (this->last_clicked_widget) {
 
			case WID_DT_CANAL: // Build canal button
 
				VpStartPlaceSizing(tile, (_game_mode == GM_EDITOR) ? VPM_X_AND_Y : VPM_X_OR_Y, DDSP_CREATE_WATER);
 
				break;
 

	
 
			case WID_DT_LOCK: // Build lock button
 
				DoCommandP(CMD_BUILD_LOCK, STR_ERROR_CAN_T_BUILD_LOCKS, CcBuildDocks, tile, 0, 0);
 
				Command<CMD_BUILD_LOCK>::Post(STR_ERROR_CAN_T_BUILD_LOCKS, CcBuildDocks, tile, 0, 0, {});
 
				break;
 

	
 
			case WID_DT_DEMOLISH: // Demolish aka dynamite button
 
				PlaceProc_DemolishArea(tile);
 
				break;
 

	
 
			case WID_DT_DEPOT: // Build depot button
 
				DoCommandP(CMD_BUILD_SHIP_DEPOT, STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, tile, _ship_depot_direction, 0);
 
				Command<CMD_BUILD_SHIP_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, tile, _ship_depot_direction, 0, {});
 
				break;
 

	
 
			case WID_DT_STATION: { // Build station button
 
				/* Determine the watery part of the dock. */
 
				DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
 
				TileIndex tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile, ReverseDiagDir(dir)) : tile);
 

	
 
				uint32 p1 = _ctrl_pressed;
 
				uint32 p2 = (uint32)INVALID_STATION << 16; // no station to join
 

	
 
				auto proc = [=](bool test, StationID to_join) -> bool {
 
					if (test) {
 
						return Command<CMD_BUILD_DOCK>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_DOCK>()), tile, p1, p2, {}).Succeeded();
 
					} else {
 
						uint32 p2_final = p2;
 
						if (to_join != INVALID_STATION) SB(p2_final, 16, 16, to_join);
 

	
 
						return DoCommandP(CMD_BUILD_DOCK, STR_ERROR_CAN_T_BUILD_DOCK_HERE, CcBuildDocks, tile, p1, p2_final);
 
						return Command<CMD_BUILD_DOCK>::Post(STR_ERROR_CAN_T_BUILD_DOCK_HERE, CcBuildDocks, tile, p1, p2_final, {});
 
					}
 
				};
 

	
 
				ShowSelectStationIfNeeded(TileArea(tile, tile_to), proc);
 
				break;
 
			}
 

	
 
			case WID_DT_BUOY: // Build buoy button
 
				DoCommandP(CMD_BUILD_BUOY, STR_ERROR_CAN_T_POSITION_BUOY_HERE, CcBuildDocks, tile, 0, 0);
 
				Command<CMD_BUILD_BUOY>::Post(STR_ERROR_CAN_T_POSITION_BUOY_HERE, CcBuildDocks, tile, 0, 0, {});
 
				break;
 

	
 
			case WID_DT_RIVER: // Build river button (in scenario editor)
 
				VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CREATE_RIVER);
 
				break;
 

	
 
			case WID_DT_BUILD_AQUEDUCT: // Build aqueduct button
 
				DoCommandP(CMD_BUILD_BRIDGE, STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE, CcBuildBridge, tile, GetOtherAqueductEnd(tile), TRANSPORT_WATER << 15);
 
				Command<CMD_BUILD_BRIDGE>::Post(STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE, CcBuildBridge, tile, GetOtherAqueductEnd(tile), TRANSPORT_WATER << 15, {});
 
				break;
 

	
 
			default: NOT_REACHED();
 
		}
 
	}
 

	
 
	void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) override
 
	{
 
		VpSelectTilesWithMethod(pt.x, pt.y, select_method);
 
	}
 

	
 
	void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) override
 
	{
 
		if (pt.x != -1) {
 
			switch (select_proc) {
 
				case DDSP_DEMOLISH_AREA:
 
					GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
 
					break;
 
				case DDSP_CREATE_WATER:
 
					DoCommandP(CMD_BUILD_CANAL, STR_ERROR_CAN_T_BUILD_CANALS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, (_game_mode == GM_EDITOR && _ctrl_pressed) ? WATER_CLASS_SEA : WATER_CLASS_CANAL);
 
					Command<CMD_BUILD_CANAL>::Post(STR_ERROR_CAN_T_BUILD_CANALS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, (_game_mode == GM_EDITOR && _ctrl_pressed) ? WATER_CLASS_SEA : WATER_CLASS_CANAL, {});
 
					break;
 
				case DDSP_CREATE_RIVER:
 
					DoCommandP(CMD_BUILD_CANAL, STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER | (_ctrl_pressed ? 1 << 2 : 0));
 
					Command<CMD_BUILD_CANAL>::Post(STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER | (_ctrl_pressed ? 1 << 2 : 0), {});
 
					break;
 

	
 
				default: break;
 
			}
 
		}
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		if (_game_mode != GM_EDITOR && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true);
 

	
 
		this->RaiseButtons();
 

	
 
		CloseWindowById(WC_BUILD_STATION, TRANSPORT_WATER);
 
		CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER);
 
		CloseWindowById(WC_SELECT_STATION, 0);
 
		CloseWindowByClass(WC_BUILD_BRIDGE);
 
	}
 

	
 
	void OnPlacePresize(Point pt, TileIndex tile_from) override
 
	{
 
		TileIndex tile_to = tile_from;
 

	
 
		if (this->last_clicked_widget == WID_DT_BUILD_AQUEDUCT) {
src/economy.cpp
Show inline comments
 
@@ -27,48 +27,49 @@
 
#include "newgrf_station.h"
 
#include "newgrf_airporttiles.h"
 
#include "object.h"
 
#include "strings_func.h"
 
#include "date_func.h"
 
#include "vehicle_func.h"
 
#include "sound_func.h"
 
#include "autoreplace_func.h"
 
#include "company_gui.h"
 
#include "signs_base.h"
 
#include "subsidy_base.h"
 
#include "subsidy_func.h"
 
#include "station_base.h"
 
#include "waypoint_base.h"
 
#include "economy_base.h"
 
#include "core/pool_func.hpp"
 
#include "core/backup_type.hpp"
 
#include "cargo_type.h"
 
#include "water.h"
 
#include "game/game.hpp"
 
#include "cargomonitor.h"
 
#include "goal_base.h"
 
#include "story_base.h"
 
#include "linkgraph/refresh.h"
 
#include "company_cmd.h"
 
#include "economy_cmd.h"
 
#include "vehicle_cmd.h"
 

	
 
#include "table/strings.h"
 
#include "table/pricebase.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
/* Initialize the cargo payment-pool */
 
CargoPaymentPool _cargo_payment_pool("CargoPayment");
 
INSTANTIATE_POOL_METHODS(CargoPayment)
 

	
 
/**
 
 * Multiply two integer values and shift the results to right.
 
 *
 
 * This function multiplies two integer values. The result is
 
 * shifted by the amount of shift to right.
 
 *
 
 * @param a The first integer
 
 * @param b The second integer
 
 * @param shift The amount to shift the value to right.
 
 * @return The shifted result
 
 */
 
@@ -608,49 +609,49 @@ static void CompanyCheckBankrupt(Company
 

	
 
		/* Bankrupt company after 6 months (if the company has no value) or latest
 
		 * after 9 months (if it still had value after 6 months) */
 
		default:
 
		case 10: {
 
			if (!_networking && _local_company == c->index) {
 
				/* If we are in singleplayer mode, leave the company playing. Eg. there
 
				 * is no THE-END, otherwise mark the client as spectator to make sure
 
				 * they are no longer in control of this company. However... when you
 
				 * join another company (cheat) the "unowned" company can bankrupt. */
 
				c->bankrupt_asked = MAX_UVALUE(CompanyMask);
 
				break;
 
			}
 

	
 
			/* Actually remove the company, but not when we're a network client.
 
			 * In case of network clients we will be getting a command from the
 
			 * server. It is done in this way as we are called from the
 
			 * StateGameLoop which can't change the current company, and thus
 
			 * updating the local company triggers an assert later on. In the
 
			 * case of a network game the command will be processed at a time
 
			 * that changing the current company is okay. In case of single
 
			 * player we are sure (the above check) that we are not the local
 
			 * company and thus we won't be moved. */
 
			if (!_networking || _network_server) {
 
				DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | (c->index << 16) | (CRR_BANKRUPT << 24), 0);
 
				Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | (c->index << 16) | (CRR_BANKRUPT << 24), 0, {});
 
				return;
 
			}
 
			break;
 
		}
 
	}
 

	
 
	if (CeilDiv(c->months_of_bankruptcy, 3) != CeilDiv(c->months_of_bankruptcy - 1, 3)) CompanyAdminUpdate(c);
 
}
 

	
 
/**
 
 * Update the finances of all companies.
 
 * Pay for the stations, update the history graph, update ratings and company values, and deal with bankruptcy.
 
 */
 
static void CompaniesGenStatistics()
 
{
 
	/* Check for bankruptcy each month */
 
	for (Company *c : Company::Iterate()) {
 
		CompanyCheckBankrupt(c);
 
	}
 

	
 
	Backup<CompanyID> cur_company(_current_company, FILE_LINE);
 

	
 
	if (!_settings_game.economy.infrastructure_maintenance) {
 
		for (const Station *st : Station::Iterate()) {
src/engine_gui.cpp
Show inline comments
 
@@ -2,48 +2,49 @@
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file engine_gui.cpp GUI to show engine related information. */
 

	
 
#include "stdafx.h"
 
#include "window_gui.h"
 
#include "engine_base.h"
 
#include "command_func.h"
 
#include "strings_func.h"
 
#include "engine_gui.h"
 
#include "articulated_vehicles.h"
 
#include "vehicle_func.h"
 
#include "company_func.h"
 
#include "rail.h"
 
#include "road.h"
 
#include "settings_type.h"
 
#include "train.h"
 
#include "roadveh.h"
 
#include "ship.h"
 
#include "aircraft.h"
 
#include "engine_cmd.h"
 

	
 
#include "widgets/engine_widget.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/**
 
 * Return the category of an engine.
 
 * @param engine Engine to examine.
 
 * @return String describing the category ("road veh", "train". "airplane", or "ship") of the engine.
 
 */
 
StringID GetEngineCategoryName(EngineID engine)
 
{
 
	const Engine *e = Engine::Get(engine);
 
	switch (e->type) {
 
		default: NOT_REACHED();
 
		case VEH_ROAD:
 
			return GetRoadTypeInfo(e->u.road.roadtype)->strings.new_engine;
 
		case VEH_AIRCRAFT:          return STR_ENGINE_PREVIEW_AIRCRAFT;
 
		case VEH_SHIP:              return STR_ENGINE_PREVIEW_SHIP;
 
		case VEH_TRAIN:
 
			return GetRailTypeInfo(e->u.rail.railtype)->strings.new_loco;
 
	}
 
@@ -104,49 +105,49 @@ struct EnginePreviewWindow : Window {
 

	
 
	void DrawWidget(const Rect &r, int widget) const override
 
	{
 
		if (widget != WID_EP_QUESTION) return;
 

	
 
		EngineID engine = this->window_number;
 
		SetDParam(0, GetEngineCategoryName(engine));
 
		int y = r.top + GetStringHeight(STR_ENGINE_PREVIEW_MESSAGE, r.right - r.left + 1);
 
		y = DrawStringMultiLine(r.left, r.right, r.top, y, STR_ENGINE_PREVIEW_MESSAGE, TC_FROMSTRING, SA_CENTER) + WD_PAR_VSEP_WIDE;
 

	
 
		SetDParam(0, engine);
 
		DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_ENGINE_NAME, TC_BLACK, SA_HOR_CENTER);
 
		y += FONT_HEIGHT_NORMAL;
 

	
 
		DrawVehicleEngine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, this->width >> 1, y + this->vehicle_space / 2, engine, GetEnginePalette(engine, _local_company), EIT_PREVIEW);
 

	
 
		y += this->vehicle_space;
 
		DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, r.bottom, GetEngineInfoString(engine), TC_FROMSTRING, SA_CENTER);
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_EP_YES:
 
				DoCommandP(CMD_WANT_ENGINE_PREVIEW, 0, this->window_number, 0);
 
				Command<CMD_WANT_ENGINE_PREVIEW>::Post(0, this->window_number, 0, {});
 
				FALLTHROUGH;
 
			case WID_EP_NO:
 
				if (!_shift_pressed) this->Close();
 
				break;
 
		}
 
	}
 

	
 
	void OnInvalidateData(int data = 0, bool gui_scope = true) override
 
	{
 
		if (!gui_scope) return;
 

	
 
		EngineID engine = this->window_number;
 
		if (Engine::Get(engine)->preview_company != _local_company) this->Close();
 
	}
 
};
 

	
 
static WindowDesc _engine_preview_desc(
 
	WDP_CENTER, "engine_preview", 0, 0,
 
	WC_ENGINE_PREVIEW, WC_NONE,
 
	WDF_CONSTRUCTION,
 
	_nested_engine_preview_widgets, lengthof(_nested_engine_preview_widgets)
 
);
 

	
 

	
src/fios_gui.cpp
Show inline comments
 
@@ -6,48 +6,49 @@
 
 */
 

	
 
/** @file fios_gui.cpp GUIs for loading/saving games, scenarios, heightmaps, ... */
 

	
 
#include "stdafx.h"
 
#include "saveload/saveload.h"
 
#include "error.h"
 
#include "gui.h"
 
#include "gfx_func.h"
 
#include "command_func.h"
 
#include "network/network.h"
 
#include "network/network_content.h"
 
#include "strings_func.h"
 
#include "fileio_func.h"
 
#include "fios.h"
 
#include "window_func.h"
 
#include "tilehighlight_func.h"
 
#include "querystring_gui.h"
 
#include "engine_func.h"
 
#include "landscape_type.h"
 
#include "date_func.h"
 
#include "core/geometry_func.hpp"
 
#include "gamelog.h"
 
#include "stringfilter_type.h"
 
#include "misc_cmd.h"
 

	
 
#include "widgets/fios_widget.h"
 

	
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
LoadCheckData _load_check_data;    ///< Data loaded from save during SL_LOAD_CHECK.
 

	
 
static bool _fios_path_changed;
 
static bool _savegame_sort_dirty;
 

	
 

	
 
/**
 
 * Reset read data.
 
 */
 
void LoadCheckData::Clear()
 
{
 
	this->checkable = false;
 
	this->error = INVALID_STRING_ID;
 
	free(this->error_data);
 
	this->error_data = nullptr;
 

	
 
@@ -337,93 +338,93 @@ public:
 

	
 
			case FT_SCENARIO:
 
				caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_SCENARIO : STR_SAVELOAD_LOAD_SCENARIO;
 
				break;
 

	
 
			case FT_HEIGHTMAP:
 
				caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_HEIGHTMAP : STR_SAVELOAD_LOAD_HEIGHTMAP;
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
		this->GetWidget<NWidgetCore>(WID_SL_CAPTION)->widget_data = caption_string;
 

	
 
		this->vscroll = this->GetScrollbar(WID_SL_SCROLLBAR);
 
		this->FinishInitNested(0);
 

	
 
		this->LowerWidget(WID_SL_DRIVES_DIRECTORIES_LIST);
 
		this->querystrings[WID_SL_FILTER] = &this->filter_editbox;
 
		this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
 

	
 
		/* pause is only used in single-player, non-editor mode, non-menu mode. It
 
		 * will be unpaused in the WE_DESTROY event handler. */
 
		if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
 
			DoCommandP(CMD_PAUSE, 0, PM_PAUSED_SAVELOAD, 1);
 
			Command<CMD_PAUSE>::Post(0, PM_PAUSED_SAVELOAD, 1, {});
 
		}
 
		SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
 

	
 
		this->OnInvalidateData(SLIWD_RESCAN_FILES);
 

	
 
		ResetObjectToPlace();
 

	
 
		/* Select the initial directory. */
 
		o_dir.type = FIOS_TYPE_DIRECT;
 
		std::string dir;
 
		switch (this->abstract_filetype) {
 
			case FT_SAVEGAME:
 
				dir = FioFindDirectory(SAVE_DIR);
 
				break;
 

	
 
			case FT_SCENARIO:
 
				dir = FioFindDirectory(SCENARIO_DIR);
 
				break;
 

	
 
			case FT_HEIGHTMAP:
 
				dir = FioFindDirectory(HEIGHTMAP_DIR);
 
				break;
 

	
 
			default:
 
				dir = _personal_dir;
 
		}
 
		strecpy(o_dir.name, dir.c_str(), lastof(o_dir.name));
 

	
 
		switch (this->fop) {
 
			case SLO_SAVE:
 
				/* Focus the edit box by default in the save window */
 
				this->SetFocusedWidget(WID_SL_SAVE_OSK_TITLE);
 
				break;
 

	
 
			default:
 
				this->SetFocusedWidget(WID_SL_FILTER);
 
		}
 
	}
 

	
 
	void Close() override
 
	{
 
		/* pause is only used in single-player, non-editor mode, non menu mode */
 
		if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
 
			DoCommandP(CMD_PAUSE, 0, PM_PAUSED_SAVELOAD, 0);
 
			Command<CMD_PAUSE>::Post(0, PM_PAUSED_SAVELOAD, 0, {});
 
		}
 
		this->Window::Close();
 
	}
 

	
 
	void DrawWidget(const Rect &r, int widget) const override
 
	{
 
		switch (widget) {
 
			case WID_SL_SORT_BYNAME:
 
			case WID_SL_SORT_BYDATE:
 
				if (((_savegame_sort_order & SORT_BY_NAME) != 0) == (widget == WID_SL_SORT_BYNAME)) {
 
					this->DrawSortButtonState(widget, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP);
 
				}
 
				break;
 

	
 
			case WID_SL_BACKGROUND: {
 
				static const char *path = nullptr;
 
				static StringID str = STR_ERROR_UNABLE_TO_READ_DRIVE;
 
				static uint64 tot = 0;
 

	
 
				if (_fios_path_changed) {
 
					str = FiosGetDescText(&path, &tot);
 
					_fios_path_changed = false;
 
				}
 

	
src/goal_gui.cpp
Show inline comments
 
@@ -2,48 +2,49 @@
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file goal_gui.cpp GUI for goals. */
 

	
 
#include "stdafx.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "window_gui.h"
 
#include "strings_func.h"
 
#include "date_func.h"
 
#include "viewport_func.h"
 
#include "gui.h"
 
#include "goal_base.h"
 
#include "core/geometry_func.hpp"
 
#include "company_func.h"
 
#include "company_base.h"
 
#include "company_gui.h"
 
#include "story_base.h"
 
#include "command_func.h"
 
#include "string_func.h"
 
#include "goal_cmd.h"
 

	
 
#include "widgets/goal_widget.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/** Goal list columns. */
 
enum GoalColumn {
 
	GC_GOAL = 0, ///< Goal text column.
 
	GC_PROGRESS, ///< Goal progress column.
 
};
 

	
 
/** Window for displaying goals. */
 
struct GoalListWindow : public Window {
 
	Scrollbar *vscroll; ///< Reference to the scrollbar widget.
 

	
 
	GoalListWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
 
	{
 
		this->CreateNestedTree();
 
		this->vscroll = this->GetScrollbar(WID_GOAL_SCROLLBAR);
 
		this->FinishInitNested(window_number);
 
		this->owner = (Owner)this->window_number;
 
		NWidgetStacked *wi = this->GetWidget<NWidgetStacked>(WID_GOAL_SELECT_BUTTONS);
 
@@ -361,59 +362,59 @@ struct GoalQuestionWindow : public Windo
 
		free(this->question);
 
	}
 

	
 
	void SetStringParameters(int widget) const override
 
	{
 
		switch (widget) {
 
			case WID_GQ_BUTTON_1:
 
				SetDParam(0, STR_GOAL_QUESTION_BUTTON_CANCEL + this->button[0]);
 
				break;
 

	
 
			case WID_GQ_BUTTON_2:
 
				SetDParam(0, STR_GOAL_QUESTION_BUTTON_CANCEL + this->button[1]);
 
				break;
 

	
 
			case WID_GQ_BUTTON_3:
 
				SetDParam(0, STR_GOAL_QUESTION_BUTTON_CANCEL + this->button[2]);
 
				break;
 
		}
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_GQ_BUTTON_1:
 
				DoCommandP(CMD_GOAL_QUESTION_ANSWER, 0, this->window_number, this->button[0]);
 
				Command<CMD_GOAL_QUESTION_ANSWER>::Post(0, this->window_number, this->button[0], {});
 
				this->Close();
 
				break;
 

	
 
			case WID_GQ_BUTTON_2:
 
				DoCommandP(CMD_GOAL_QUESTION_ANSWER, 0, this->window_number, this->button[1]);
 
				Command<CMD_GOAL_QUESTION_ANSWER>::Post(0, this->window_number, this->button[1], {});
 
				this->Close();
 
				break;
 

	
 
			case WID_GQ_BUTTON_3:
 
				DoCommandP(CMD_GOAL_QUESTION_ANSWER, 0, this->window_number, this->button[2]);
 
				Command<CMD_GOAL_QUESTION_ANSWER>::Post(0, this->window_number, this->button[2], {});
 
				this->Close();
 
				break;
 
		}
 
	}
 

	
 
	void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
 
	{
 
		if (widget != WID_GQ_QUESTION) return;
 

	
 
		SetDParamStr(0, this->question);
 
		size->height = GetStringHeight(STR_JUST_RAW_STRING, size->width) + WD_PAR_VSEP_WIDE;
 
	}
 

	
 
	void DrawWidget(const Rect &r, int widget) const override
 
	{
 
		if (widget != WID_GQ_QUESTION) return;
 

	
 
		SetDParamStr(0, this->question);
 
		DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, STR_JUST_RAW_STRING, this->colour, SA_TOP | SA_HOR_CENTER);
 
	}
 
};
 

	
 
/** Widgets of the goal question window. */
 
static const NWidgetPart _nested_goal_question_widgets_question[] = {
src/group_gui.cpp
Show inline comments
 
@@ -5,48 +5,49 @@
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file group_gui.cpp GUI for the group window. */
 

	
 
#include "stdafx.h"
 
#include "textbuf_gui.h"
 
#include "command_func.h"
 
#include "vehicle_gui.h"
 
#include "vehicle_base.h"
 
#include "string_func.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "vehicle_func.h"
 
#include "autoreplace_gui.h"
 
#include "company_func.h"
 
#include "widgets/dropdown_func.h"
 
#include "tilehighlight_func.h"
 
#include "vehicle_gui_base.h"
 
#include "core/geometry_func.hpp"
 
#include "company_base.h"
 
#include "company_gui.h"
 
#include "gui.h"
 
#include "group_cmd.h"
 
#include "vehicle_cmd.h"
 

	
 
#include "widgets/group_widget.h"
 

	
 
#include "table/sprites.h"
 

	
 
#include "safeguards.h"
 

	
 
static const int LEVEL_WIDTH = 10; ///< Indenting width of a sub-group in pixels
 

	
 
typedef GUIList<const Group*> GUIGroupList;
 

	
 
static const NWidgetPart _nested_group_widgets[] = {
 
	NWidget(NWID_HORIZONTAL), // Window header
 
		NWidget(WWT_CLOSEBOX, COLOUR_GREY),
 
		NWidget(WWT_CAPTION, COLOUR_GREY, WID_GL_CAPTION),
 
		NWidget(WWT_SHADEBOX, COLOUR_GREY),
 
		NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
 
		NWidget(WWT_STICKYBOX, COLOUR_GREY),
 
	EndContainer(),
 
	NWidget(NWID_HORIZONTAL),
 
		/* left part */
 
		NWidget(NWID_VERTICAL),
 
			NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_ALL_VEHICLES), SetFill(1, 0), EndContainer(),
 
			NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_DEFAULT_VEHICLES), SetFill(1, 0), EndContainer(),
 
@@ -620,49 +621,49 @@ public:
 
			case WID_GL_LIST_VEHICLE:
 
				if (this->vli.index != ALL_GROUP && this->grouping == GB_NONE) {
 
					/* Mark vehicles which are in sub-groups (only if we are not using shared order coalescing) */
 
					int y = r.top;
 
					uint max = static_cast<uint>(std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->vehgroups.size()));
 
					for (uint i = this->vscroll->GetPosition(); i < max; ++i) {
 
						const Vehicle *v = this->vehgroups[i].GetSingleVehicle();
 
						if (v->group_id != this->vli.index) {
 
							GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->resize.step_height - 2, _colour_gradient[COLOUR_GREY][3], FILLRECT_CHECKER);
 
						}
 
						y += this->resize.step_height;
 
					}
 
				}
 

	
 
				this->DrawVehicleListItems(this->vehicle_sel, this->resize.step_height, r);
 
				break;
 
		}
 
	}
 

	
 
	static void DeleteGroupCallback(Window *win, bool confirmed)
 
	{
 
		if (confirmed) {
 
			VehicleGroupWindow *w = (VehicleGroupWindow*)win;
 
			w->vli.index = ALL_GROUP;
 
			DoCommandP(CMD_DELETE_GROUP, STR_ERROR_GROUP_CAN_T_DELETE, (TileIndex)0, w->group_confirm, 0);
 
			Command<CMD_DELETE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_DELETE, 0, w->group_confirm, 0, {});
 
		}
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_GL_SORT_BY_ORDER: // Flip sorting method ascending/descending
 
				this->vehgroups.ToggleSortOrder();
 
				this->SetDirty();
 
				break;
 

	
 
			case WID_GL_GROUP_BY_DROPDOWN: // Select grouping option dropdown menu
 
				ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_GL_GROUP_BY_DROPDOWN, 0, 0);
 
				return;
 

	
 
			case WID_GL_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
 
				ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(),  WID_GL_SORT_BY_DROPDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
 
				return;
 

	
 
			case WID_GL_ALL_VEHICLES: // All vehicles button
 
				if (!IsAllGroupID(this->vli.index)) {
 
					this->vli.index = ALL_GROUP;
 
					this->vehgroups.ForceRebuild();
 
					this->SetDirty();
 
@@ -751,145 +752,145 @@ public:
 
						ShowOrdersWindow(v);
 
					} else {
 
						this->vehicle_sel = v->index;
 

	
 
						if (_ctrl_pressed && this->grouping == GB_NONE) {
 
							/*
 
							 * It only makes sense to select a group if not using shared orders
 
							 * since two vehicles sharing orders can be from different groups.
 
							 */
 
							this->SelectGroup(v->group_id);
 
						}
 

	
 
						SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
 
						SetMouseCursorVehicle(v, EIT_IN_LIST);
 
						_cursor.vehchain = true;
 

	
 
						this->SetDirty();
 
					}
 
				}
 

	
 
				break;
 
			}
 

	
 
			case WID_GL_CREATE_GROUP: { // Create a new group
 
				DoCommandP(CMD_CREATE_GROUP, STR_ERROR_GROUP_CAN_T_CREATE, CcCreateGroup, 0, this->vli.vtype, this->vli.index);
 
				Command<CMD_CREATE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_CREATE, CcCreateGroup, 0, this->vli.vtype, this->vli.index, {});
 
				break;
 
			}
 

	
 
			case WID_GL_DELETE_GROUP: { // Delete the selected group
 
				this->group_confirm = this->vli.index;
 
				ShowQuery(STR_QUERY_GROUP_DELETE_CAPTION, STR_GROUP_DELETE_QUERY_TEXT, this, DeleteGroupCallback);
 
				break;
 
			}
 

	
 
			case WID_GL_RENAME_GROUP: // Rename the selected roup
 
				this->ShowRenameGroupWindow(this->vli.index, false);
 
				break;
 

	
 
			case WID_GL_LIVERY_GROUP: // Set group livery
 
				ShowCompanyLiveryWindow(this->owner, this->vli.index);
 
				break;
 

	
 
			case WID_GL_AVAILABLE_VEHICLES:
 
				ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
 
				break;
 

	
 
			case WID_GL_MANAGE_VEHICLES_DROPDOWN: {
 
				ShowDropDownList(this, this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index)), 0, WID_GL_MANAGE_VEHICLES_DROPDOWN);
 
				break;
 
			}
 

	
 
			case WID_GL_START_ALL:
 
			case WID_GL_STOP_ALL: { // Start/stop all vehicles of the list
 
				DoCommandP(CMD_MASS_START_STOP, 0, (1 << 1) | (widget == WID_GL_START_ALL ? (1 << 0) : 0), this->vli.Pack());
 
				Command<CMD_MASS_START_STOP>::Post(0, (1 << 1) | (widget == WID_GL_START_ALL ? (1 << 0) : 0), this->vli.Pack(), {});
 
				break;
 
			}
 

	
 
			case WID_GL_REPLACE_PROTECTION: {
 
				const Group *g = Group::GetIfValid(this->vli.index);
 
				if (g != nullptr) {
 
					DoCommandP(CMD_SET_GROUP_FLAG, 0, this->vli.index | (GroupFlags::GF_REPLACE_PROTECTION << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION) ? 0 : 1) | (_ctrl_pressed << 1));
 
					Command<CMD_SET_GROUP_FLAG>::Post(0, this->vli.index | (GroupFlags::GF_REPLACE_PROTECTION << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION) ? 0 : 1) | (_ctrl_pressed << 1), {});
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnDragDrop_Group(Point pt, int widget)
 
	{
 
		const Group *g = Group::Get(this->group_sel);
 

	
 
		switch (widget) {
 
			case WID_GL_ALL_VEHICLES: // All vehicles
 
			case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles
 
				if (g->parent != INVALID_GROUP) {
 
					DoCommandP(CMD_ALTER_GROUP, STR_ERROR_GROUP_CAN_T_SET_PARENT, 0, this->group_sel | (1 << 16), INVALID_GROUP);
 
					Command<CMD_ALTER_GROUP>::Post(STR_ERROR_GROUP_CAN_T_SET_PARENT, 0, this->group_sel | (1 << 16), INVALID_GROUP, {});
 
				}
 

	
 
				this->group_sel = INVALID_GROUP;
 
				this->group_over = INVALID_GROUP;
 
				this->SetDirty();
 
				break;
 

	
 
			case WID_GL_LIST_GROUP: { // Matrix group
 
				uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
 
				GroupID new_g = id_g >= this->groups.size() ? INVALID_GROUP : this->groups[id_g]->index;
 

	
 
				if (this->group_sel != new_g && g->parent != new_g) {
 
					DoCommandP(CMD_ALTER_GROUP, STR_ERROR_GROUP_CAN_T_SET_PARENT, 0, this->group_sel | (1 << 16), new_g);
 
					Command<CMD_ALTER_GROUP>::Post(STR_ERROR_GROUP_CAN_T_SET_PARENT, 0, this->group_sel | (1 << 16), new_g, {});
 
				}
 

	
 
				this->group_sel = INVALID_GROUP;
 
				this->group_over = INVALID_GROUP;
 
				this->SetDirty();
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnDragDrop_Vehicle(Point pt, int widget)
 
	{
 
		switch (widget) {
 
			case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles
 
				DoCommandP(CMD_ADD_VEHICLE_GROUP, STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, 0, DEFAULT_GROUP, this->vehicle_sel | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0));
 
				Command<CMD_ADD_VEHICLE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, 0, DEFAULT_GROUP, this->vehicle_sel | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), {});
 

	
 
				this->vehicle_sel = INVALID_VEHICLE;
 
				this->group_over = INVALID_GROUP;
 

	
 
				this->SetDirty();
 
				break;
 

	
 
			case WID_GL_LIST_GROUP: { // Matrix group
 
				const VehicleID vindex = this->vehicle_sel;
 
				this->vehicle_sel = INVALID_VEHICLE;
 
				this->group_over = INVALID_GROUP;
 
				this->SetDirty();
 

	
 
				uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
 
				GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index;
 

	
 
				DoCommandP(CMD_ADD_VEHICLE_GROUP, STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, 0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0));
 
				Command<CMD_ADD_VEHICLE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, 0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), {});
 
				break;
 
			}
 

	
 
			case WID_GL_LIST_VEHICLE: { // Matrix vehicle
 
				const VehicleID vindex = this->vehicle_sel;
 
				this->vehicle_sel = INVALID_VEHICLE;
 
				this->group_over = INVALID_GROUP;
 
				this->SetDirty();
 

	
 
				uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_VEHICLE);
 
				if (id_v >= this->vehgroups.size()) return; // click out of list bound
 

	
 
				const GUIVehicleGroup &vehgroup = this->vehgroups[id_v];
 
				switch (this->grouping) {
 
					case GB_NONE: {
 
						const Vehicle *v = vehgroup.GetSingleVehicle();
 
						if (!VehicleClicked(v) && vindex == v->index) {
 
							ShowVehicleViewWindow(v);
 
						}
 
						break;
 
					}
 

	
 
					case GB_SHARED_ORDERS: {
 
						const Vehicle *v = vehgroup.vehicles_begin[0];
 
@@ -902,91 +903,91 @@ public:
 
								ShowVehicleListWindow(v);
 
							}
 
						}
 
						break;
 
					}
 

	
 
					default:
 
						NOT_REACHED();
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnDragDrop(Point pt, int widget) override
 
	{
 
		if (this->vehicle_sel != INVALID_VEHICLE) OnDragDrop_Vehicle(pt, widget);
 
		if (this->group_sel != INVALID_GROUP) OnDragDrop_Group(pt, widget);
 

	
 
		_cursor.vehchain = false;
 
	}
 

	
 
	void OnQueryTextFinished(char *str) override
 
	{
 
		if (str != nullptr) DoCommandP(CMD_ALTER_GROUP, STR_ERROR_GROUP_CAN_T_RENAME, 0, this->group_rename, 0, str);
 
		if (str != nullptr) Command<CMD_ALTER_GROUP>::Post(STR_ERROR_GROUP_CAN_T_RENAME, 0, this->group_rename, 0, str);
 
		this->group_rename = INVALID_GROUP;
 
	}
 

	
 
	void OnResize() override
 
	{
 
		this->group_sb->SetCapacityFromWidget(this, WID_GL_LIST_GROUP);
 
		this->vscroll->SetCapacityFromWidget(this, WID_GL_LIST_VEHICLE);
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		switch (widget) {
 
			case WID_GL_GROUP_BY_DROPDOWN:
 
				this->UpdateVehicleGroupBy(static_cast<GroupBy>(index));
 
				break;
 

	
 
			case WID_GL_SORT_BY_DROPDOWN:
 
				this->vehgroups.SetSortType(index);
 
				break;
 

	
 
			case WID_GL_MANAGE_VEHICLES_DROPDOWN:
 
				assert(this->vehicles.size() != 0);
 

	
 
				switch (index) {
 
					case ADI_REPLACE: // Replace window
 
						ShowReplaceGroupVehicleWindow(this->vli.index, this->vli.vtype);
 
						break;
 
					case ADI_SERVICE: // Send for servicing
 
					case ADI_DEPOT: { // Send to Depots
 
						DoCommandP(CMD_SEND_VEHICLE_TO_DEPOT, GetCmdSendToDepotMsg(this->vli.vtype), 0, DEPOT_MASS_SEND | (index == ADI_SERVICE ? DEPOT_SERVICE : 0U), this->vli.Pack());
 
						Command<CMD_SEND_VEHICLE_TO_DEPOT>::Post(GetCmdSendToDepotMsg(this->vli.vtype), 0, DEPOT_MASS_SEND | (index == ADI_SERVICE ? DEPOT_SERVICE : 0U), this->vli.Pack(), {});
 
						break;
 
					}
 

	
 
					case ADI_ADD_SHARED: // Add shared Vehicles
 
						assert(Group::IsValidID(this->vli.index));
 

	
 
						DoCommandP(CMD_ADD_SHARED_VEHICLE_GROUP, STR_ERROR_GROUP_CAN_T_ADD_SHARED_VEHICLE, 0, this->vli.index, this->vli.vtype);
 
						Command<CMD_ADD_SHARED_VEHICLE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_ADD_SHARED_VEHICLE, 0, this->vli.index, this->vli.vtype, {});
 
						break;
 
					case ADI_REMOVE_ALL: // Remove all Vehicles from the selected group
 
						assert(Group::IsValidID(this->vli.index));
 

	
 
						DoCommandP(CMD_REMOVE_ALL_VEHICLES_GROUP, STR_ERROR_GROUP_CAN_T_REMOVE_ALL_VEHICLES, (TileIndex)0, this->vli.index, 0);
 
						Command<CMD_REMOVE_ALL_VEHICLES_GROUP>::Post(STR_ERROR_GROUP_CAN_T_REMOVE_ALL_VEHICLES, 0, this->vli.index, 0, {});
 
						break;
 
					default: NOT_REACHED();
 
				}
 
				break;
 

	
 
			default: NOT_REACHED();
 
		}
 

	
 
		this->SetDirty();
 
	}
 

	
 
	void OnGameTick() override
 
	{
 
		if (this->groups.NeedResort() || this->vehgroups.NeedResort()) {
 
			this->SetDirty();
 
		}
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		/* abort drag & drop */
 
		this->vehicle_sel = INVALID_VEHICLE;
 
		this->DirtyHighlightedGroupWidget();
 
		this->group_sel = INVALID_GROUP;
src/highscore_gui.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file highscore_gui.cpp Definition of the HighScore and EndGame windows */
 

	
 
#include "stdafx.h"
 
#include "highscore.h"
 
#include "table/strings.h"
 
#include "gfx_func.h"
 
#include "table/sprites.h"
 
#include "window_gui.h"
 
#include "window_func.h"
 
#include "network/network.h"
 
#include "command_func.h"
 
#include "company_func.h"
 
#include "company_base.h"
 
#include "strings_func.h"
 
#include "hotkeys.h"
 
#include "zoom_func.h"
 
#include "misc_cmd.h"
 

	
 
#include "widgets/highscore_widget.h"
 

	
 
#include "safeguards.h"
 

	
 
struct EndGameHighScoreBaseWindow : Window {
 
	uint32 background_img;
 
	int8 rank;
 

	
 
	EndGameHighScoreBaseWindow(WindowDesc *desc) : Window(desc)
 
	{
 
		this->InitNested();
 
		CLRBITS(this->flags, WF_WHITE_BORDER);
 
		ResizeWindow(this, _screen.width - this->width, _screen.height - this->height);
 
	}
 

	
 
	/* Always draw a maximized window and within it the centered background */
 
	void SetupHighScoreEndWindow()
 
	{
 
		/* Resize window to "full-screen". */
 
		if (this->width != _screen.width || this->height != _screen.height) ResizeWindow(this, _screen.width - this->width, _screen.height - this->height);
 

	
 
		this->DrawWidgets();
 

	
 
@@ -75,127 +76,127 @@ struct EndGameHighScoreBaseWindow : Wind
 
		if (IsQuitKey(keycode)) return ES_NOT_HANDLED;
 

	
 
		switch (keycode) {
 
			/* Keys for telling we want to go on */
 
			case WKC_RETURN:
 
			case WKC_ESC:
 
			case WKC_SPACE:
 
				this->Close();
 
				return ES_HANDLED;
 

	
 
			default:
 
				/* We want to handle all keys; we don't want windows in
 
				 * the background to open. Especially the ones that do
 
				 * locate themselves based on the status-/toolbars. */
 
				return ES_HANDLED;
 
		}
 
	}
 
};
 

	
 
/** End game window shown at the end of the game */
 
struct EndGameWindow : EndGameHighScoreBaseWindow {
 
	EndGameWindow(WindowDesc *desc) : EndGameHighScoreBaseWindow(desc)
 
	{
 
		/* Pause in single-player to have a look at the highscore at your own leisure */
 
		if (!_networking) DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 1);
 
		if (!_networking) Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 1, {});
 

	
 
		this->background_img = SPR_TYCOON_IMG1_BEGIN;
 

	
 
		if (_local_company != COMPANY_SPECTATOR) {
 
			const Company *c = Company::Get(_local_company);
 
			if (c->old_economy[0].performance_history == SCORE_MAX) {
 
				this->background_img = SPR_TYCOON_IMG2_BEGIN;
 
			}
 
		}
 

	
 
		/* In a network game show the endscores of the custom difficulty 'network' which is
 
		 * a TOP5 of that game, and not an all-time TOP5. */
 
		if (_networking) {
 
			this->window_number = SP_MULTIPLAYER;
 
			this->rank = SaveHighScoreValueNetwork();
 
		} else {
 
			/* in singleplayer mode _local company is always valid */
 
			const Company *c = Company::Get(_local_company);
 
			this->window_number = SP_CUSTOM;
 
			this->rank = SaveHighScoreValue(c);
 
		}
 

	
 
		MarkWholeScreenDirty();
 
	}
 

	
 
	void Close() override
 
	{
 
		if (!_networking) DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 0); // unpause
 
		if (!_networking) Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 0, {}); // unpause
 
		ShowHighscoreTable(this->window_number, this->rank);
 
		this->EndGameHighScoreBaseWindow::Close();
 
	}
 

	
 
	void OnPaint() override
 
	{
 
		this->SetupHighScoreEndWindow();
 
		Point pt = this->GetTopLeft(ScaleGUITrad(640), ScaleGUITrad(480));
 

	
 
		const Company *c = Company::GetIfValid(_local_company);
 
		if (c == nullptr) return;
 

	
 
		/* We need to get performance from last year because the image is shown
 
		 * at the start of the new year when these things have already been copied */
 
		if (this->background_img == SPR_TYCOON_IMG2_BEGIN) { // Tycoon of the century \o/
 
			SetDParam(0, c->index);
 
			SetDParam(1, c->index);
 
			SetDParam(2, EndGameGetPerformanceTitleFromValue(c->old_economy[0].performance_history));
 
			DrawStringMultiLine(pt.x + ScaleGUITrad(15), pt.x + ScaleGUITrad(640) - ScaleGUITrad(25), pt.y + ScaleGUITrad(90), pt.y + ScaleGUITrad(160), STR_HIGHSCORE_PRESIDENT_OF_COMPANY_ACHIEVES_STATUS, TC_FROMSTRING, SA_CENTER);
 
		} else {
 
			SetDParam(0, c->index);
 
			SetDParam(1, EndGameGetPerformanceTitleFromValue(c->old_economy[0].performance_history));
 
			DrawStringMultiLine(pt.x + ScaleGUITrad(36), pt.x + ScaleGUITrad(640), pt.y + ScaleGUITrad(140), pt.y + ScaleGUITrad(206), STR_HIGHSCORE_COMPANY_ACHIEVES_STATUS, TC_FROMSTRING, SA_CENTER);
 
		}
 
	}
 
};
 

	
 
struct HighScoreWindow : EndGameHighScoreBaseWindow {
 
	bool game_paused_by_player; ///< True if the game was paused by the player when the highscore window was opened.
 

	
 
	HighScoreWindow(WindowDesc *desc, int difficulty, int8 ranking) : EndGameHighScoreBaseWindow(desc)
 
	{
 
		/* pause game to show the chart */
 
		this->game_paused_by_player = _pause_mode == PM_PAUSED_NORMAL;
 
		if (!_networking && !this->game_paused_by_player) DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 1);
 
		if (!_networking && !this->game_paused_by_player) Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 1, {});
 

	
 
		/* Close all always on-top windows to get a clean screen */
 
		if (_game_mode != GM_MENU) HideVitalWindows();
 

	
 
		MarkWholeScreenDirty();
 
		this->window_number = difficulty; // show highscore chart for difficulty...
 
		this->background_img = SPR_HIGHSCORE_CHART_BEGIN; // which background to show
 
		this->rank = ranking;
 
	}
 

	
 
	void Close() override
 
	{
 
		if (_game_mode != GM_MENU) ShowVitalWindows();
 

	
 
		if (!_networking && !this->game_paused_by_player) DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 0); // unpause
 
		if (!_networking && !this->game_paused_by_player) Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 0, {}); // unpause
 

	
 
		this->EndGameHighScoreBaseWindow::Close();
 
	}
 

	
 
	void OnPaint() override
 
	{
 
		const HighScore *hs = _highscore_table[this->window_number];
 

	
 
		this->SetupHighScoreEndWindow();
 
		Point pt = this->GetTopLeft(ScaleGUITrad(640), ScaleGUITrad(480));
 

	
 
		SetDParam(0, _settings_game.game_creation.ending_year);
 
		DrawStringMultiLine(pt.x + ScaleGUITrad(70), pt.x + ScaleGUITrad(570), pt.y, pt.y + ScaleGUITrad(140), !_networking ? STR_HIGHSCORE_TOP_COMPANIES_WHO_REACHED : STR_HIGHSCORE_TOP_COMPANIES_NETWORK_GAME, TC_FROMSTRING, SA_CENTER);
 

	
 
		/* Draw Highscore peepz */
 
		for (uint8 i = 0; i < lengthof(_highscore_table[0]); i++) {
 
			SetDParam(0, i + 1);
 
			DrawString(pt.x + ScaleGUITrad(40), pt.x + ScaleGUITrad(600), pt.y + ScaleGUITrad(140 + i * 55), STR_HIGHSCORE_POSITION);
 

	
 
			if (hs[i].company[0] != '\0') {
 
				TextColour colour = (this->rank == i) ? TC_RED : TC_BLACK; // draw new highscore in red
 

	
 
				SetDParamStr(0, hs[i].company);
 
				DrawString(pt.x + ScaleGUITrad(71), pt.x + ScaleGUITrad(569), pt.y + ScaleGUITrad(140 + i * 55), STR_JUST_BIG_RAW_STRING, colour);
src/industry_gui.cpp
Show inline comments
 
@@ -18,48 +18,49 @@
 
#include "viewport_func.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "cheat_type.h"
 
#include "newgrf_industries.h"
 
#include "newgrf_text.h"
 
#include "newgrf_debug.h"
 
#include "network/network.h"
 
#include "strings_func.h"
 
#include "company_func.h"
 
#include "tilehighlight_func.h"
 
#include "string_func.h"
 
#include "sortlist_type.h"
 
#include "widgets/dropdown_func.h"
 
#include "company_base.h"
 
#include "core/geometry_func.hpp"
 
#include "core/random_func.hpp"
 
#include "core/backup_type.hpp"
 
#include "genworld.h"
 
#include "smallmap_gui.h"
 
#include "widgets/dropdown_type.h"
 
#include "widgets/industry_widget.h"
 
#include "clear_map.h"
 
#include "zoom_func.h"
 
#include "industry_cmd.h"
 

	
 
#include "table/strings.h"
 

	
 
#include <bitset>
 

	
 
#include "safeguards.h"
 

	
 
bool _ignore_restrictions;
 
std::bitset<NUM_INDUSTRYTYPES> _displayed_industries; ///< Communication from the industry chain window to the smallmap window about what industries to display.
 

	
 
/** Cargo suffix type (for which window is it requested) */
 
enum CargoSuffixType {
 
	CST_FUND,  ///< Fund-industry window
 
	CST_VIEW,  ///< View-industry window
 
	CST_DIR,   ///< Industry-directory window
 
};
 

	
 
/** Ways of displaying the cargo. */
 
enum CargoSuffixDisplay {
 
	CSD_CARGO,             ///< Display the cargo without sub-type (cb37 result 401).
 
	CSD_CARGO_AMOUNT,      ///< Display the cargo and amount (if useful), but no sub-type (cb37 result 400 or fail).
 
	CSD_CARGO_TEXT,        ///< Display then cargo and supplied string (cb37 result 800-BFF).
 
	CSD_CARGO_AMOUNT_TEXT, ///< Display then cargo, amount, and string (cb37 result 000-3FF).
 
};
 
@@ -658,92 +659,92 @@ public:
 
					this->SetDirty();
 

	
 
					if (_thd.GetCallbackWnd() == this &&
 
							((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != nullptr && indsp->IsRawIndustry()) ||
 
							this->selected_type == INVALID_INDUSTRYTYPE ||
 
							!this->enabled[this->selected_index])) {
 
						/* Reset the button state if going to prospecting or "build many industries" */
 
						this->RaiseButtons();
 
						ResetObjectToPlace();
 
					}
 

	
 
					this->SetButtons();
 
					if (this->enabled[this->selected_index] && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
 
				}
 
				break;
 
			}
 

	
 
			case WID_DPI_DISPLAY_WIDGET:
 
				if (this->selected_type != INVALID_INDUSTRYTYPE) ShowIndustryCargoesWindow(this->selected_type);
 
				break;
 

	
 
			case WID_DPI_FUND_WIDGET: {
 
				if (this->selected_type != INVALID_INDUSTRYTYPE) {
 
					if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
 
						DoCommandP(CMD_BUILD_INDUSTRY, STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, 0, this->selected_type, InteractiveRandom());
 
						Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, 0, this->selected_type, InteractiveRandom(), {});
 
						this->HandleButtonClick(WID_DPI_FUND_WIDGET);
 
					} else {
 
						HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
 
					}
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnResize() override
 
	{
 
		/* Adjust the number of items in the matrix depending of the resize */
 
		this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
 
	}
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		bool success = true;
 
		/* We do not need to protect ourselves against "Random Many Industries" in this mode */
 
		const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
 
		uint32 seed = InteractiveRandom();
 
		uint32 layout_index = InteractiveRandomRange((uint32)indsp->layouts.size());
 

	
 
		if (_game_mode == GM_EDITOR) {
 
			/* Show error if no town exists at all */
 
			if (Town::GetNumItems() == 0) {
 
				SetDParam(0, indsp->name);
 
				ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
 
				return;
 
			}
 

	
 
			Backup<CompanyID> cur_company(_current_company, OWNER_NONE, FILE_LINE);
 
			Backup<bool> old_generating_world(_generating_world, true, FILE_LINE);
 
			_ignore_restrictions = true;
 

	
 
			DoCommandP(CMD_BUILD_INDUSTRY, STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, &CcBuildIndustry, tile, (layout_index << 8) | this->selected_type, seed);
 
			Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, &CcBuildIndustry, tile, (layout_index << 8) | this->selected_type, seed, {});
 

	
 
			cur_company.Restore();
 
			old_generating_world.Restore();
 
			_ignore_restrictions = false;
 
		} else {
 
			success = DoCommandP(CMD_BUILD_INDUSTRY, STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, (layout_index << 8) | this->selected_type, seed);
 
			success = Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, (layout_index << 8) | this->selected_type, seed, {});
 
		}
 

	
 
		/* If an industry has been built, just reset the cursor and the system */
 
		if (success && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
	}
 

	
 
	void OnHundredthTick() override
 
	{
 
		if (_game_mode == GM_EDITOR) return;
 
		const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
 

	
 
		if (indsp->enabled) {
 
			bool call_back_result = GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0;
 

	
 
			/* Only if result does match the previous state would it require a redraw. */
 
			if (call_back_result != this->enabled[this->selected_index]) {
 
				this->enabled[this->selected_index] = call_back_result;
 
				this->SetButtons();
 
				this->SetDirty();
 
			}
 
		}
 
	}
 

	
 
	void OnTimeout() override
src/linkgraph/linkgraphschedule.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file linkgraphschedule.cpp Definition of link graph schedule used for cargo distribution. */
 

	
 
#include "../stdafx.h"
 
#include "linkgraphschedule.h"
 
#include "init.h"
 
#include "demands.h"
 
#include "mcf.h"
 
#include "flowmapper.h"
 
#include "../framerate_type.h"
 
#include "../command_func.h"
 
#include "../network/network.h"
 
#include "../misc_cmd.h"
 

	
 
#include "../safeguards.h"
 

	
 
/**
 
 * Static instance of LinkGraphSchedule.
 
 * Note: This instance is created on task start.
 
 *       Lazy creation on first usage results in a data race between the CDist threads.
 
 */
 
/* static */ LinkGraphSchedule LinkGraphSchedule::instance;
 

	
 
/**
 
 * Start the next job in the schedule.
 
 */
 
void LinkGraphSchedule::SpawnNext()
 
{
 
	if (this->schedule.empty()) return;
 
	LinkGraph *next = this->schedule.front();
 
	LinkGraph *first = next;
 
	while (next->Size() < 2) {
 
		this->schedule.splice(this->schedule.end(), this->schedule, this->schedule.begin());
 
		next = this->schedule.front();
 
		if (next == first) return;
 
	}
 
	assert(next == LinkGraph::Get(next->index));
 
@@ -152,57 +153,57 @@ LinkGraphSchedule::LinkGraphSchedule()
 

	
 
/**
 
 * Delete a link graph schedule and its handlers.
 
 */
 
LinkGraphSchedule::~LinkGraphSchedule()
 
{
 
	this->Clear();
 
	for (uint i = 0; i < lengthof(this->handlers); ++i) {
 
		delete this->handlers[i];
 
	}
 
}
 

	
 
/**
 
 * Pause the game if in 2 _date_fract ticks, we would do a join with the next
 
 * link graph job, but it is still running.
 
 * The check is done 2 _date_fract ticks early instead of 1, as in multiplayer
 
 * calls to DoCommandP are executed after a delay of 1 _date_fract tick.
 
 * If we previously paused, unpause if the job is now ready to be joined with.
 
 */
 
void StateGameLoop_LinkGraphPauseControl()
 
{
 
	if (_pause_mode & PM_PAUSED_LINK_GRAPH) {
 
		/* We are paused waiting on a job, check the job every tick. */
 
		if (!LinkGraphSchedule::instance.IsJoinWithUnfinishedJobDue()) {
 
			DoCommandP(CMD_PAUSE, 0, PM_PAUSED_LINK_GRAPH, 0);
 
			Command<CMD_PAUSE>::Post(0, PM_PAUSED_LINK_GRAPH, 0, {});
 
		}
 
	} else if (_pause_mode == PM_UNPAUSED &&
 
			_date_fract == LinkGraphSchedule::SPAWN_JOIN_TICK - 2 &&
 
			_date % _settings_game.linkgraph.recalc_interval == _settings_game.linkgraph.recalc_interval / 2 &&
 
			LinkGraphSchedule::instance.IsJoinWithUnfinishedJobDue()) {
 
		/* Perform check two _date_fract ticks before we would join, to make
 
		 * sure it also works in multiplayer. */
 
		DoCommandP(CMD_PAUSE, 0, PM_PAUSED_LINK_GRAPH, 1);
 
		Command<CMD_PAUSE>::Post(0, PM_PAUSED_LINK_GRAPH, 1, {});
 
	}
 
}
 

	
 
/**
 
 * Pause the game on load if we would do a join with the next link graph job,
 
 * but it is still running, and it would not be caught by a call to
 
 * StateGameLoop_LinkGraphPauseControl().
 
 */
 
void AfterLoad_LinkGraphPauseControl()
 
{
 
	if (LinkGraphSchedule::instance.IsJoinWithUnfinishedJobDue()) {
 
		_pause_mode |= PM_PAUSED_LINK_GRAPH;
 
	}
 
}
 

	
 
/**
 
 * Spawn or join a link graph job or compress a link graph if any link graph is
 
 * due to do so.
 
 */
 
void OnTick_LinkGraph()
 
{
 
	if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return;
 
	Date offset = _date % _settings_game.linkgraph.recalc_interval;
 
	if (offset == 0) {
src/main_gui.cpp
Show inline comments
 
@@ -12,48 +12,49 @@
 
#include "spritecache.h"
 
#include "window_gui.h"
 
#include "window_func.h"
 
#include "textbuf_gui.h"
 
#include "viewport_func.h"
 
#include "command_func.h"
 
#include "console_gui.h"
 
#include "progress.h"
 
#include "transparency_gui.h"
 
#include "map_func.h"
 
#include "sound_func.h"
 
#include "transparency.h"
 
#include "strings_func.h"
 
#include "zoom_func.h"
 
#include "company_base.h"
 
#include "company_func.h"
 
#include "toolbar_gui.h"
 
#include "statusbar_gui.h"
 
#include "linkgraph/linkgraph_gui.h"
 
#include "tilehighlight_func.h"
 
#include "hotkeys.h"
 
#include "guitimer_func.h"
 
#include "error.h"
 
#include "news_gui.h"
 
#include "misc_cmd.h"
 

	
 
#include "saveload/saveload.h"
 

	
 
#include "widgets/main_widget.h"
 

	
 
#include "network/network.h"
 
#include "network/network_func.h"
 
#include "network/network_gui.h"
 
#include "network/network_base.h"
 

	
 
#include "table/sprites.h"
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/**
 
 * This code is shared for the majority of the pushbuttons.
 
 * Handles e.g. the pressing of a button (to build things), playing of click sound and sets certain parameters
 
 *
 
 * @param w Window which called the function
 
 * @param widget ID of the widget (=button) that called this function
 
 * @param cursor How should the cursor image change? E.g. cursor with depot image in it
 
 * @param mode Tile highlighting mode, e.g. drawing a rectangle or a dot on the ground
 
 * @return true if the button is clicked, false if it's unclicked
 
@@ -305,49 +306,49 @@ struct MainWindow : Window
 
		switch (hotkey) {
 
			case GHK_CENTER:
 
			case GHK_CENTER_ZOOM: {
 
				Point pt = GetTileBelowCursor();
 
				if (pt.x != -1) {
 
					bool instant = (hotkey == GHK_CENTER_ZOOM && this->viewport->zoom != _settings_client.gui.zoom_min);
 
					if (hotkey == GHK_CENTER_ZOOM) MaxZoomInOut(ZOOM_IN, this);
 
					ScrollMainWindowTo(pt.x, pt.y, -1, instant);
 
				}
 
				break;
 
			}
 

	
 
			case GHK_RESET_OBJECT_TO_PLACE: ResetObjectToPlace(); break;
 
			case GHK_DELETE_WINDOWS: CloseNonVitalWindows(); break;
 
			case GHK_DELETE_NONVITAL_WINDOWS: CloseAllNonVitalWindows(); break;
 
			case GHK_DELETE_ALL_MESSAGES: DeleteAllMessages(); break;
 
			case GHK_REFRESH_SCREEN: MarkWholeScreenDirty(); break;
 

	
 
			case GHK_CRASH: // Crash the game
 
				*(volatile byte *)nullptr = 0;
 
				break;
 

	
 
			case GHK_MONEY: // Gimme money
 
				/* You can only cheat for money in singleplayer mode. */
 
				if (!_networking) DoCommandP(CMD_MONEY_CHEAT, 0, 10000000, 0);
 
				if (!_networking) Command<CMD_MONEY_CHEAT>::Post(0, 10000000, 0, {});
 
				break;
 

	
 
			case GHK_UPDATE_COORDS: // Update the coordinates of all station signs
 
				UpdateAllVirtCoords();
 
				break;
 

	
 
			case GHK_TOGGLE_TRANSPARENCY:
 
			case GHK_TOGGLE_TRANSPARENCY + 1:
 
			case GHK_TOGGLE_TRANSPARENCY + 2:
 
			case GHK_TOGGLE_TRANSPARENCY + 3:
 
			case GHK_TOGGLE_TRANSPARENCY + 4:
 
			case GHK_TOGGLE_TRANSPARENCY + 5:
 
			case GHK_TOGGLE_TRANSPARENCY + 6:
 
			case GHK_TOGGLE_TRANSPARENCY + 7:
 
			case GHK_TOGGLE_TRANSPARENCY + 8:
 
				/* Transparency toggle hot keys */
 
				ToggleTransparency((TransparencyOption)(hotkey - GHK_TOGGLE_TRANSPARENCY));
 
				MarkWholeScreenDirty();
 
				break;
 

	
 
			case GHK_TOGGLE_INVISIBILITY:
 
			case GHK_TOGGLE_INVISIBILITY + 1:
 
			case GHK_TOGGLE_INVISIBILITY + 2:
 
			case GHK_TOGGLE_INVISIBILITY + 3:
src/misc_cmd.cpp
Show inline comments
 
@@ -114,49 +114,49 @@ CommandCost CmdDecreaseLoan(DoCommandFla
 
	}
 

	
 
	if (c->money < loan) {
 
		SetDParam(0, loan);
 
		return_cmd_error(STR_ERROR_CURRENCY_REQUIRED);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		c->money        -= loan;
 
		c->current_loan -= loan;
 
		InvalidateCompanyWindows(c);
 
	}
 
	return CommandCost();
 
}
 

	
 
/**
 
 * In case of an unsafe unpause, we want the
 
 * user to confirm that it might crash.
 
 * @param w         unused
 
 * @param confirmed whether the user confirmed their action
 
 */
 
static void AskUnsafeUnpauseCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) {
 
		DoCommandP(CMD_PAUSE, 0, PM_PAUSED_ERROR, 0);
 
		Command<CMD_PAUSE>::Post(0, PM_PAUSED_ERROR, 0, {});
 
	}
 
}
 

	
 
/**
 
 * Pause/Unpause the game (server-only).
 
 * Set or unset a bit in the pause mode. If pause mode is zero the game is
 
 * unpaused. A bitset is used instead of a boolean value/counter to have
 
 * more control over the game when saving/loading, etc.
 
 * @param flags operation to perform
 
 * @param tile unused
 
 * @param p1 the pause mode to change
 
 * @param p2 1 pauses, 0 unpauses this mode
 
 * @param text unused
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdPause(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	switch (p1) {
 
		case PM_PAUSED_SAVELOAD:
 
		case PM_PAUSED_ERROR:
 
		case PM_PAUSED_NORMAL:
 
		case PM_PAUSED_GAME_SCRIPT:
 
		case PM_PAUSED_LINK_GRAPH:
 
			break;
src/network/network.cpp
Show inline comments
 
@@ -374,49 +374,49 @@ void NetworkHandlePauseChange(PauseMode 
 
			}
 

	
 
			NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE, CC_DEFAULT, false, "", GetString(str));
 
			break;
 
		}
 

	
 
		default:
 
			return;
 
	}
 
}
 

	
 

	
 
/**
 
 * Helper function for the pause checkers. If pause is true and the
 
 * current pause mode isn't set the game will be paused, if it it false
 
 * and the pause mode is set the game will be unpaused. In the other
 
 * cases nothing happens to the pause state.
 
 * @param pause whether we'd like to pause
 
 * @param pm the mode which we would like to pause with
 
 */
 
static void CheckPauseHelper(bool pause, PauseMode pm)
 
{
 
	if (pause == ((_pause_mode & pm) != PM_UNPAUSED)) return;
 

	
 
	DoCommandP(CMD_PAUSE, 0, pm, pause ? 1 : 0);
 
	Command<CMD_PAUSE>::Post(0, pm, pause ? 1 : 0, {});
 
}
 

	
 
/**
 
 * Counts the number of active clients connected.
 
 * It has to be in STATUS_ACTIVE and not a spectator
 
 * @return number of active clients
 
 */
 
static uint NetworkCountActiveClients()
 
{
 
	uint count = 0;
 

	
 
	for (const NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
 
		if (cs->status != NetworkClientSocket::STATUS_ACTIVE) continue;
 
		if (!Company::IsValidID(cs->GetInfo()->client_playas)) continue;
 
		count++;
 
	}
 

	
 
	return count;
 
}
 

	
 
/**
 
 * Check if the minimum number of active clients has been reached and pause or unpause the game as appropriate
 
 */
 
static void CheckMinActiveClients()
src/network/network_command.cpp
Show inline comments
 
@@ -430,26 +430,26 @@ static inline void SanitizeSingleStringH
 
template<class Ttuple, size_t... Tindices>
 
static inline void SanitizeStringsHelper(CommandFlags cmd_flags, Ttuple &values, std::index_sequence<Tindices...>)
 
{
 
	((SanitizeSingleStringHelper(cmd_flags, std::get<Tindices>(values))), ...);
 
}
 

	
 
/**
 
 * Validate and sanitize strings in command data.
 
 * @tparam Tcmd Command this data belongs to.
 
 * @param data Command data.
 
 * @return Sanitized command data.
 
 */
 
template <Commands Tcmd>
 
CommandDataBuffer SanitizeCmdStrings(const CommandDataBuffer &data)
 
{
 
	auto args = EndianBufferReader::ToValue<typename CommandTraits<Tcmd>::Args>(data);
 
	SanitizeStringsHelper(CommandTraits<Tcmd>::flags, args, std::make_index_sequence<std::tuple_size_v<typename CommandTraits<Tcmd>::Args>>{});
 
	return EndianBufferWriter<CommandDataBuffer>::FromValue(args);
 
}
 

	
 
template <Commands Tcmd>
 
void UnpackNetworkCommand(const CommandPacket *cp)
 
{
 
	auto args = EndianBufferReader::ToValue<typename CommandTraits<Tcmd>::Args>(cp->data);
 
	std::apply(&InjectNetworkCommand, std::tuple_cat(std::make_tuple(Tcmd, cp->err_msg, cp->callback, cp->my_cmd), args));
 
	Command<Tcmd>::PostFromNet(cp->err_msg, cp->callback, cp->my_cmd, cp->tile, args);
 
}
src/network/network_gui.cpp
Show inline comments
 
@@ -15,48 +15,49 @@
 
#include "network_gui.h"
 
#include "network_gamelist.h"
 
#include "network.h"
 
#include "network_base.h"
 
#include "network_content.h"
 
#include "network_server.h"
 
#include "network_coordinator.h"
 
#include "../gui.h"
 
#include "network_udp.h"
 
#include "../window_func.h"
 
#include "../gfx_func.h"
 
#include "../widgets/dropdown_type.h"
 
#include "../widgets/dropdown_func.h"
 
#include "../querystring_gui.h"
 
#include "../sortlist_type.h"
 
#include "../company_func.h"
 
#include "../command_func.h"
 
#include "../core/geometry_func.hpp"
 
#include "../genworld.h"
 
#include "../map_type.h"
 
#include "../guitimer_func.h"
 
#include "../zoom_func.h"
 
#include "../sprite.h"
 
#include "../settings_internal.h"
 
#include "../company_cmd.h"
 

	
 
#include "../widgets/network_widget.h"
 

	
 
#include "table/strings.h"
 
#include "../table/sprites.h"
 

	
 
#include "../stringfilter_type.h"
 

	
 
#ifdef __EMSCRIPTEN__
 
#	include <emscripten.h>
 
#endif
 

	
 
#include <map>
 

	
 
#include "../safeguards.h"
 

	
 
static void ShowNetworkStartServerWindow();
 

	
 
static const int NETWORK_LIST_REFRESH_DELAY = 30; ///< Time, in seconds, between updates of the network list.
 

	
 
static ClientID _admin_client_id = INVALID_CLIENT_ID; ///< For what client a confirmation window is open.
 
static CompanyID _admin_company_id = INVALID_COMPANY; ///< For what company a confirmation window is open.
 

	
 
/**
 
@@ -1374,49 +1375,49 @@ enum DropDownAdmin {
 
static void AdminClientKickCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) NetworkServerKickClient(_admin_client_id, {});
 
}
 

	
 
/**
 
 * Callback function for admin command to ban client.
 
 * @param w The window which initiated the confirmation dialog.
 
 * @param confirmed Iff the user pressed Yes.
 
 */
 
static void AdminClientBanCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) NetworkServerKickOrBanIP(_admin_client_id, true, {});
 
}
 

	
 
/**
 
 * Callback function for admin command to reset company.
 
 * @param w The window which initiated the confirmation dialog.
 
 * @param confirmed Iff the user pressed Yes.
 
 */
 
static void AdminCompanyResetCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) {
 
		if (NetworkCompanyHasClients(_admin_company_id)) return;
 
		DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | _admin_company_id << 16 | CRR_MANUAL << 24, 0);
 
		Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | _admin_company_id << 16 | CRR_MANUAL << 24, 0, {});
 
	}
 
}
 

	
 
/**
 
 * Callback function for admin command to unlock company.
 
 * @param w The window which initiated the confirmation dialog.
 
 * @param confirmed Iff the user pressed Yes.
 
 */
 
static void AdminCompanyUnlockCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) NetworkServerSetCompanyPassword(_admin_company_id, "", false);
 
}
 

	
 
/**
 
 * Button shown for either a company or client in the client-list.
 
 *
 
 * These buttons are dynamic and strongly depends on which company/client
 
 * what buttons are available. This class allows dynamically creating them
 
 * as the current Widget system does not.
 
 */
 
class ButtonCommon {
 
public:
 
	SpriteID sprite;   ///< The sprite to use on the button.
 
	StringID tooltip;  ///< The tooltip of the button.
 
@@ -1514,49 +1515,49 @@ private:
 
	 */
 
	static void OnClickCompanyJoin(NetworkClientListWindow *w, Point pt, CompanyID company_id)
 
	{
 
		if (_network_server) {
 
			NetworkServerDoMove(CLIENT_ID_SERVER, company_id);
 
			MarkWholeScreenDirty();
 
		} else if (NetworkCompanyIsPassworded(company_id)) {
 
			w->query_widget = WID_CL_COMPANY_JOIN;
 
			w->join_company = company_id;
 
			ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, w, CS_ALPHANUMERAL, QSF_PASSWORD);
 
		} else {
 
			NetworkClientRequestMove(company_id);
 
		}
 
	}
 

	
 
	/**
 
	 * Crete new company button is clicked.
 
	 * @param w The instance of this window.
 
	 * @param pt The point where this button was clicked.
 
	 * @param company_id The company this button was assigned to.
 
	 */
 
	static void OnClickCompanyNew(NetworkClientListWindow *w, Point pt, CompanyID company_id)
 
	{
 
		if (_network_server) {
 
			DoCommandP(CMD_COMPANY_CTRL, 0, CCA_NEW, _network_own_client_id);
 
			Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW, _network_own_client_id, {});
 
		} else {
 
			NetworkSendCommand(CMD_COMPANY_CTRL, STR_NULL, nullptr, _local_company, 0, CCA_NEW, 0, {});
 
		}
 
	}
 

	
 
	/**
 
	 * Admin button on a Client is clicked.
 
	 * @param w The instance of this window.
 
	 * @param pt The point where this button was clicked.
 
	 * @param client_id The client this button was assigned to.
 
	 */
 
	static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id)
 
	{
 
		DropDownList list;
 
		list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK, DD_CLIENT_ADMIN_KICK, false));
 
		list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN, DD_CLIENT_ADMIN_BAN, false));
 

	
 
		Rect wi_rect;
 
		wi_rect.left   = pt.x;
 
		wi_rect.right  = pt.x;
 
		wi_rect.top    = pt.y;
 
		wi_rect.bottom = pt.y;
 

	
 
		w->dd_client_id = client_id;
src/network/network_server.cpp
Show inline comments
 
@@ -8,48 +8,49 @@
 
/** @file network_server.cpp Server part of the network protocol. */
 

	
 
#include "../stdafx.h"
 
#include "../strings_func.h"
 
#include "../date_func.h"
 
#include "core/game_info.h"
 
#include "network_admin.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "network_base.h"
 
#include "../console_func.h"
 
#include "../company_base.h"
 
#include "../command_func.h"
 
#include "../saveload/saveload.h"
 
#include "../saveload/saveload_filter.h"
 
#include "../station_base.h"
 
#include "../genworld.h"
 
#include "../company_func.h"
 
#include "../company_gui.h"
 
#include "../company_cmd.h"
 
#include "../roadveh.h"
 
#include "../order_backup.h"
 
#include "../core/pool_func.hpp"
 
#include "../core/random_func.hpp"
 
#include "../company_cmd.h"
 
#include "../rev.h"
 
#include <mutex>
 
#include <condition_variable>
 

	
 
#include "../safeguards.h"
 

	
 

	
 
/* This file handles all the server-commands */
 

	
 
DECLARE_POSTFIX_INCREMENT(ClientID)
 
/** The identifier counter for new clients (is never decreased) */
 
static ClientID _network_client_id = CLIENT_ID_FIRST;
 

	
 
/** Make very sure the preconditions given in network_type.h are actually followed */
 
static_assert(MAX_CLIENT_SLOTS > MAX_CLIENTS);
 
/** Yes... */
 
static_assert(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENT_SLOTS);
 

	
 
/** The pool with clients. */
 
NetworkClientSocketPool _networkclientsocket_pool("NetworkClientSocket");
 
INSTANTIATE_POOL_METHODS(NetworkClientSocket)
 

	
 
/** Instantiate the listen sockets. */
 
template SocketList TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED>::sockets;
 
@@ -1534,63 +1535,63 @@ static void NetworkAutoCleanCompanies()
 
		if (Company::IsValidID(ci->client_playas)) clients_in_company[ci->client_playas] = true;
 
	}
 

	
 
	if (_settings_client.network.autoclean_novehicles != 0) {
 
		memset(vehicles_in_company, 0, sizeof(vehicles_in_company));
 

	
 
		for (const Vehicle *v : Vehicle::Iterate()) {
 
			if (!Company::IsValidID(v->owner) || !v->IsPrimaryVehicle()) continue;
 
			vehicles_in_company[v->owner]++;
 
		}
 
	}
 

	
 
	/* Go through all the companies */
 
	for (const Company *c : Company::Iterate()) {
 
		/* Skip the non-active once */
 
		if (c->is_ai) continue;
 

	
 
		if (!clients_in_company[c->index]) {
 
			/* The company is empty for one month more */
 
			_network_company_states[c->index].months_empty++;
 

	
 
			/* Is the company empty for autoclean_unprotected-months, and is there no protection? */
 
			if (_settings_client.network.autoclean_unprotected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_unprotected && _network_company_states[c->index].password.empty()) {
 
				/* Shut the company down */
 
				DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0);
 
				Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0, {});
 
				IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no password.", c->index + 1);
 
			}
 
			/* Is the company empty for autoclean_protected-months, and there is a protection? */
 
			if (_settings_client.network.autoclean_protected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_protected && !_network_company_states[c->index].password.empty()) {
 
				/* Unprotect the company */
 
				_network_company_states[c->index].password.clear();
 
				IConsolePrint(CC_INFO, "Auto-removed protection from company #{}.", c->index + 1);
 
				_network_company_states[c->index].months_empty = 0;
 
				NetworkServerUpdateCompanyPassworded(c->index, false);
 
			}
 
			/* Is the company empty for autoclean_novehicles-months, and has no vehicles? */
 
			if (_settings_client.network.autoclean_novehicles != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_novehicles && vehicles_in_company[c->index] == 0) {
 
				/* Shut the company down */
 
				DoCommandP(CMD_COMPANY_CTRL, 0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0);
 
				Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0, {});
 
				IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no vehicles.", c->index + 1);
 
			}
 
		} else {
 
			/* It is not empty, reset the date */
 
			_network_company_states[c->index].months_empty = 0;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Check whether a name is unique, and otherwise try to make it unique.
 
 * @param new_name The name to check/modify.
 
 * @return True if an unique name was achieved.
 
 */
 
bool NetworkMakeClientNameUnique(std::string &name)
 
{
 
	bool is_name_unique = false;
 
	std::string original_name = name;
 

	
 
	for (uint number = 1; !is_name_unique && number <= MAX_CLIENTS; number++) {  // Something's really wrong when there're more names than clients
 
		is_name_unique = true;
 
		for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) {
 
			if (ci->client_name == name) {
 
				/* Name already in use */
src/object_gui.cpp
Show inline comments
 
@@ -4,48 +4,49 @@
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file object_gui.cpp The GUI for objects. */
 

	
 
#include "stdafx.h"
 
#include "command_func.h"
 
#include "hotkeys.h"
 
#include "newgrf.h"
 
#include "newgrf_object.h"
 
#include "newgrf_text.h"
 
#include "object.h"
 
#include "querystring_gui.h"
 
#include "sortlist_type.h"
 
#include "stringfilter_type.h"
 
#include "string_func.h"
 
#include "strings_func.h"
 
#include "viewport_func.h"
 
#include "tilehighlight_func.h"
 
#include "window_gui.h"
 
#include "window_func.h"
 
#include "zoom_func.h"
 
#include "terraform_cmd.h"
 
#include "object_cmd.h"
 

	
 
#include "widgets/object_widget.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
static ObjectClassID _selected_object_class; ///< Currently selected available object class.
 
static int _selected_object_index;           ///< Index of the currently selected object if existing, else \c -1.
 
static uint8 _selected_object_view;          ///< the view of the selected object
 

	
 
/** Enum referring to the Hotkeys in the build object window */
 
enum BuildObjectHotkeys {
 
	BOHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
 
};
 

	
 
/** The window used for building objects. */
 
class BuildObjectWindow : public Window {
 
	typedef GUIList<ObjectClassID, StringFilter &> GUIObjectClassList; ///< Type definition for the list to hold available object classes.
 

	
 
	static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.
 
	static const int OBJECT_MARGIN = 4;    ///< The margin (in pixels) around an object.
 

	
 
	int line_height;                       ///< The height of a single line.
 
@@ -521,50 +522,50 @@ public:
 
				this->SelectOtherClass(this->object_classes[num_clicked]);
 
				this->SelectFirstAvailableObject(false);
 
				break;
 
			}
 

	
 
			case WID_BO_SELECT_IMAGE: {
 
				ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
 
				int num_clicked = objclass->GetIndexFromUI(GB(widget, 16, 16));
 
				if (num_clicked >= 0 && objclass->GetSpec(num_clicked)->IsAvailable()) this->SelectOtherObject(num_clicked);
 
				break;
 
			}
 

	
 
			case WID_BO_OBJECT_SPRITE:
 
				if (_selected_object_index != -1) {
 
					_selected_object_view = GB(widget, 16, 16);
 
					this->SelectOtherObject(_selected_object_index); // Re-select the object for a different view.
 
				}
 
				break;
 
		}
 
	}
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
 
		DoCommandP(CMD_BUILD_OBJECT, STR_ERROR_CAN_T_BUILD_OBJECT, CcTerraform,
 
				tile, objclass->GetSpec(_selected_object_index)->Index(), _selected_object_view);
 
		Command<CMD_BUILD_OBJECT>::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcTerraform,
 
				tile, objclass->GetSpec(_selected_object_index)->Index(), _selected_object_view, {});
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		this->UpdateButtons(_selected_object_class, -1, _selected_object_view);
 
	}
 

	
 
	EventState OnHotkey(int hotkey) override
 
	{
 
		switch (hotkey) {
 
			case BOHK_FOCUS_FILTER_BOX:
 
				this->SetFocusedWidget(WID_BO_FILTER);
 
				SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
 
				break;
 

	
 
			default:
 
				return ES_NOT_HANDLED;
 
		}
 

	
 
		return ES_HANDLED;
 
	}
 

	
 
	void OnEditboxChanged(int wid) override
 
	{
src/openttd.cpp
Show inline comments
 
@@ -45,48 +45,49 @@
 
#include "roadstop_base.h"
 
#include "elrail_func.h"
 
#include "rev.h"
 
#include "highscore.h"
 
#include "station_base.h"
 
#include "crashlog.h"
 
#include "engine_func.h"
 
#include "core/random_func.hpp"
 
#include "rail_gui.h"
 
#include "road_gui.h"
 
#include "core/backup_type.hpp"
 
#include "hotkeys.h"
 
#include "newgrf.h"
 
#include "misc/getoptdata.h"
 
#include "game/game.hpp"
 
#include "game/game_config.hpp"
 
#include "town.h"
 
#include "subsidy_func.h"
 
#include "gfx_layout.h"
 
#include "viewport_func.h"
 
#include "viewport_sprite_sorter.h"
 
#include "framerate_type.h"
 
#include "industry.h"
 
#include "network/network_gui.h"
 
#include "misc_cmd.h"
 

	
 
#include "linkgraph/linkgraphschedule.h"
 

	
 
#include <stdarg.h>
 
#include <system_error>
 

	
 
#include "safeguards.h"
 

	
 
#ifdef __EMSCRIPTEN__
 
#	include <emscripten.h>
 
#	include <emscripten/html5.h>
 
#endif
 

	
 
void CallLandscapeTick();
 
void IncreaseDate();
 
void DoPaletteAnimations();
 
void MusicLoop();
 
void ResetMusic();
 
void CallWindowGameTickEvent();
 
bool HandleBootstrap();
 

	
 
extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY);
 
extern void ShowOSErrorBox(const char *buf, bool system);
 
extern std::string _config_file;
 
@@ -830,78 +831,78 @@ void HandleExitGameRequest()
 

	
 
/**
 
 * Triggers everything that should be triggered when starting a game.
 
 * @param dedicated_server Whether this is a dedicated server or not.
 
 */
 
static void OnStartGame(bool dedicated_server)
 
{
 
	/* Update the local company for a loaded game. It is either always
 
	 * company #1 (eg 0) or in the case of a dedicated server a spectator */
 
	SetLocalCompany(dedicated_server ? COMPANY_SPECTATOR : COMPANY_FIRST);
 

	
 
	/* Update the static game info to set the values from the new game. */
 
	NetworkServerUpdateGameInfo();
 
	/* Execute the game-start script */
 
	IConsoleCmdExec("exec scripts/game_start.scr 0");
 
}
 

	
 
static void MakeNewGameDone()
 
{
 
	SettingsDisableElrail(_settings_game.vehicle.disable_elrails);
 

	
 
	/* In a dedicated server, the server does not play */
 
	if (!VideoDriver::GetInstance()->HasGUI()) {
 
		OnStartGame(true);
 
		if (_settings_client.gui.pause_on_newgame) DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 1);
 
		if (_settings_client.gui.pause_on_newgame) Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 1, {});
 
		return;
 
	}
 

	
 
	/* Create a single company */
 
	DoStartupNewCompany(false);
 

	
 
	Company *c = Company::Get(COMPANY_FIRST);
 
	c->settings = _settings_client.company;
 

	
 
	/* Overwrite color from settings if needed
 
	 * COLOUR_END corresponds to Random colour */
 
	if (_settings_client.gui.starting_colour != COLOUR_END) {
 
		c->colour = _settings_client.gui.starting_colour;
 
		ResetCompanyLivery(c);
 
		_company_colours[c->index] = (Colours)c->colour;
 
	}
 

	
 
	OnStartGame(false);
 

	
 
	InitializeRailGUI();
 
	InitializeRoadGUI();
 

	
 
	/* We are the server, we start a new company (not dedicated),
 
	 * so set the default password *if* needed. */
 
	if (_network_server && !_settings_client.network.default_company_pass.empty()) {
 
		NetworkChangeCompanyPassword(_local_company, _settings_client.network.default_company_pass);
 
	}
 

	
 
	if (_settings_client.gui.pause_on_newgame) DoCommandP(CMD_PAUSE, 0, PM_PAUSED_NORMAL, 1);
 
	if (_settings_client.gui.pause_on_newgame) Command<CMD_PAUSE>::Post(0, PM_PAUSED_NORMAL, 1, {});
 

	
 
	CheckEngines();
 
	CheckIndustries();
 
	MarkWholeScreenDirty();
 

	
 
	if (_network_server && !_network_dedicated) ShowClientList();
 
}
 

	
 
static void MakeNewGame(bool from_heightmap, bool reset_settings)
 
{
 
	_game_mode = GM_NORMAL;
 
	if (!from_heightmap) {
 
		/* "reload" command needs to know what mode we were in. */
 
		_file_to_saveload.SetMode(SLO_INVALID, FT_INVALID, DFT_INVALID);
 
	}
 

	
 
	ResetGRFConfig(true);
 

	
 
	GenerateWorldSetCallback(&MakeNewGameDone);
 
	GenerateWorld(from_heightmap ? GWM_HEIGHTMAP : GWM_NEWGAME, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y, reset_settings);
 
}
 

	
 
static void MakeNewEditorWorldDone()
 
{
 
@@ -1024,71 +1025,71 @@ void SwitchToMode(SwitchMode new_mode)
 
			}
 

	
 
			MakeNewGame(false, new_mode == SM_NEWGAME);
 
			break;
 

	
 
		case SM_RESTARTGAME: // Restart --> 'Random game' with current settings
 
		case SM_NEWGAME: // New Game --> 'Random game'
 
			MakeNewGame(false, new_mode == SM_NEWGAME);
 
			break;
 

	
 
		case SM_LOAD_GAME: { // Load game, Play Scenario
 
			ResetGRFConfig(true);
 
			ResetWindowSystem();
 

	
 
			if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, NO_DIRECTORY)) {
 
				SetDParamStr(0, GetSaveLoadErrorString());
 
				ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
 
			} else {
 
				if (_file_to_saveload.abstract_ftype == FT_SCENARIO) {
 
					/* Reset engine pool to simplify changing engine NewGRFs in scenario editor. */
 
					EngineOverrideManager::ResetToCurrentNewGRFConfig();
 
				}
 
				OnStartGame(_network_dedicated);
 
				/* Decrease pause counter (was increased from opening load dialog) */
 
				DoCommandP(CMD_PAUSE, 0, PM_PAUSED_SAVELOAD, 0);
 
				Command<CMD_PAUSE>::Post(0, PM_PAUSED_SAVELOAD, 0, {});
 
			}
 
			break;
 
		}
 

	
 
		case SM_RESTART_HEIGHTMAP: // Load a heightmap and start a new game from it with current settings
 
		case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it
 
			MakeNewGame(true, new_mode == SM_START_HEIGHTMAP);
 
			break;
 

	
 
		case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor
 
			SetLocalCompany(OWNER_NONE);
 

	
 
			GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
 
			MarkWholeScreenDirty();
 
			break;
 

	
 
		case SM_LOAD_SCENARIO: { // Load scenario from scenario editor
 
			if (SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_EDITOR, NO_DIRECTORY)) {
 
				SetLocalCompany(OWNER_NONE);
 
				_settings_newgame.game_creation.starting_year = _cur_year;
 
				/* Cancel the saveload pausing */
 
				DoCommandP(CMD_PAUSE, 0, PM_PAUSED_SAVELOAD, 0);
 
				Command<CMD_PAUSE>::Post(0, PM_PAUSED_SAVELOAD, 0, {});
 
			} else {
 
				SetDParamStr(0, GetSaveLoadErrorString());
 
				ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
 
			}
 
			break;
 
		}
 

	
 
		case SM_JOIN_GAME: // Join a multiplayer game
 
			LoadIntroGame();
 
			NetworkClientJoinGame();
 
			break;
 

	
 
		case SM_MENU: // Switch to game intro menu
 
			LoadIntroGame();
 
			if (BaseSounds::ini_set.empty() && BaseSounds::GetUsedSet()->fallback && SoundDriver::GetInstance()->HasOutput()) {
 
				ShowErrorMessage(STR_WARNING_FALLBACK_SOUNDSET, INVALID_STRING_ID, WL_CRITICAL);
 
				BaseSounds::ini_set = BaseSounds::GetUsedSet()->name;
 
			}
 
			break;
 

	
 
		case SM_SAVE_GAME: // Save game.
 
			/* Make network saved games on pause compatible to singleplayer mode */
 
			if (SaveOrLoad(_file_to_saveload.name, SLO_SAVE, DFT_GAME_FILE, NO_DIRECTORY) != SL_OK) {
 
				SetDParamStr(0, GetSaveLoadErrorString());
src/order_backup.cpp
Show inline comments
 
@@ -150,49 +150,49 @@ void OrderBackup::DoRestore(Vehicle *v)
 
 * @return The cost of this operation or an error.
 
 */
 
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	/* No need to check anything. If the tile or user don't exist we just ignore it. */
 
	if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, p2);
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Reset an user's OrderBackup if needed.
 
 * @param user The user associated with the OrderBackup.
 
 * @pre _network_server.
 
 * @note Must not be used from a command.
 
 */
 
/* static */ void OrderBackup::ResetUser(uint32 user)
 
{
 
	assert(_network_server);
 

	
 
	for (OrderBackup *ob : OrderBackup::Iterate()) {
 
		/* If it's not a backup of us, ignore it. */
 
		if (ob->user != user) continue;
 

	
 
		DoCommandP(CMD_CLEAR_ORDER_BACKUP, 0, 0, user);
 
		Command<CMD_CLEAR_ORDER_BACKUP>::Post(0, 0, user, {});
 
		return;
 
	}
 
}
 

	
 
/**
 
 * Reset the OrderBackups from GUI/game logic.
 
 * @param t        The tile of the order backup.
 
 * @param from_gui Whether the call came from the GUI, i.e. whether
 
 *                 it must be synced over the network.
 
 */
 
/* static */ void OrderBackup::Reset(TileIndex t, bool from_gui)
 
{
 
	/* The user has CLIENT_ID_SERVER as default when network play is not active,
 
	 * but compiled it. A network client has its own variable for the unique
 
	 * client/user identifier. Finally if networking isn't compiled in the
 
	 * default is just plain and simple: 0. */
 
	uint32 user = _networking && !_network_server ? _network_own_client_id : CLIENT_ID_SERVER;
 

	
 
	for (OrderBackup *ob : OrderBackup::Iterate()) {
 
		/* If it's not a backup of us, ignore it. */
 
		if (ob->user != user) continue;
 
		/* If it's not for our chosen tile either, ignore it. */
 
		if (t != INVALID_TILE && t != ob->tile) continue;
 

	
src/order_gui.cpp
Show inline comments
 
@@ -8,48 +8,49 @@
 
/** @file order_gui.cpp GUI related to orders. */
 

	
 
#include "stdafx.h"
 
#include "command_func.h"
 
#include "viewport_func.h"
 
#include "depot_map.h"
 
#include "roadveh.h"
 
#include "timetable.h"
 
#include "strings_func.h"
 
#include "company_func.h"
 
#include "widgets/dropdown_type.h"
 
#include "widgets/dropdown_func.h"
 
#include "textbuf_gui.h"
 
#include "string_func.h"
 
#include "tilehighlight_func.h"
 
#include "network/network.h"
 
#include "station_base.h"
 
#include "industry.h"
 
#include "waypoint_base.h"
 
#include "core/geometry_func.hpp"
 
#include "hotkeys.h"
 
#include "aircraft.h"
 
#include "engine_func.h"
 
#include "vehicle_func.h"
 
#include "order_cmd.h"
 

	
 
#include "widgets/order_widget.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
/** Order load types that could be given to station orders. */
 
static const StringID _station_load_types[][5][5] = {
 
	{
 
		/* No refitting. */
 
		{
 
			STR_EMPTY,
 
			INVALID_STRING_ID,
 
			STR_ORDER_FULL_LOAD,
 
			STR_ORDER_FULL_LOAD_ANY,
 
			STR_ORDER_NO_LOAD,
 
		}, {
 
			STR_ORDER_UNLOAD,
 
			INVALID_STRING_ID,
 
			STR_ORDER_UNLOAD_FULL_LOAD,
 
			STR_ORDER_UNLOAD_FULL_LOAD_ANY,
 
			STR_ORDER_UNLOAD_NO_LOAD,
 
		}, {
 
			STR_ORDER_TRANSFER,
 
@@ -570,195 +571,195 @@ private:
 
			HT_VEHICLE,           // OPOS_SHARE
 
		};
 
		SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, goto_place_style[type - 1], this);
 
		this->goto_type = type;
 
		this->SetWidgetDirty(WID_O_GOTO);
 
	}
 

	
 
	/**
 
	 * Handle the click on the full load button.
 
	 * @param load_type Load flag to apply. If matches existing load type, toggles to default of 'load if possible'.
 
	 * @param toggle If we toggle or not (used for hotkey behavior)
 
	 */
 
	void OrderClick_FullLoad(OrderLoadFlags load_type, bool toggle = false)
 
	{
 
		VehicleOrderID sel_ord = this->OrderGetSel();
 
		const Order *order = this->vehicle->GetOrder(sel_ord);
 

	
 
		if (order == nullptr) return;
 

	
 
		if (toggle && order->GetLoadType() == load_type) {
 
			load_type = OLF_LOAD_IF_POSSIBLE; // reset to 'default'
 
		}
 
		if (order->GetLoadType() == load_type) return; // If we still match, do nothing
 

	
 
		DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (load_type << 4));
 
		Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (load_type << 4), {});
 
	}
 

	
 
	/**
 
	 * Handle the click on the service.
 
	 */
 
	void OrderClick_Service(int i)
 
	{
 
		VehicleOrderID sel_ord = this->OrderGetSel();
 

	
 
		if (i < 0) {
 
			const Order *order = this->vehicle->GetOrder(sel_ord);
 
			if (order == nullptr) return;
 
			i = (order->GetDepotOrderType() & ODTFB_SERVICE) ? DA_ALWAYS_GO : DA_SERVICE;
 
		}
 
		DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_DEPOT_ACTION | (i << 4));
 
		Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_DEPOT_ACTION | (i << 4), {});
 
	}
 

	
 
	/**
 
	 * Handle the click on the service in nearest depot button.
 
	 */
 
	void OrderClick_NearestDepot()
 
	{
 
		Order order;
 
		order.next = nullptr;
 
		order.index = 0;
 
		order.MakeGoToDepot(0, ODTFB_PART_OF_ORDERS,
 
				_settings_client.gui.new_nonstop && this->vehicle->IsGroundVehicle() ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
 
		order.SetDepotActionType(ODATFB_NEAREST_DEPOT);
 

	
 
		DoCommandP(CMD_INSERT_ORDER, STR_ERROR_CAN_T_INSERT_NEW_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack());
 
		Command<CMD_INSERT_ORDER>::Post(STR_ERROR_CAN_T_INSERT_NEW_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), {});
 
	}
 

	
 
	/**
 
	 * Handle the click on the unload button.
 
	 * @param unload_type Unload flag to apply. If matches existing unload type, toggles to default of 'unload if possible'.
 
	 * @param toggle If we toggle or not (used for hotkey behavior)
 
	 */
 
	void OrderClick_Unload(OrderUnloadFlags unload_type, bool toggle = false)
 
	{
 
		VehicleOrderID sel_ord = this->OrderGetSel();
 
		const Order *order = this->vehicle->GetOrder(sel_ord);
 

	
 
		if (order == nullptr) return;
 

	
 
		if (toggle && order->GetUnloadType() == unload_type) {
 
			unload_type = OUF_UNLOAD_IF_POSSIBLE;
 
		}
 
		if (order->GetUnloadType() == unload_type) return; // If we still match, do nothing
 

	
 
		DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_UNLOAD | (unload_type << 4));
 
		Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_UNLOAD | (unload_type << 4), {});
 

	
 
		/* Transfer and unload orders with leave empty as default */
 
		if (unload_type == OUFB_TRANSFER || unload_type == OUFB_UNLOAD) {
 
			DoCommandP(CMD_MODIFY_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (OLFB_NO_LOAD << 4));
 
			Command<CMD_MODIFY_ORDER>::Post(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (OLFB_NO_LOAD << 4), {});
 
			this->SetWidgetDirty(WID_O_FULL_LOAD);
 
		}
 
	}
 

	
 
	/**
 
	 * Handle the click on the nonstop button.
 
	 * @param non_stop what non-stop type to use; -1 to use the 'next' one.
 
	 */
 
	void OrderClick_Nonstop(int non_stop)
 
	{
 
		if (!this->vehicle->IsGroundVehicle()) return;
 

	
 
		VehicleOrderID sel_ord = this->OrderGetSel();
 
		const Order *order = this->vehicle->GetOrder(sel_ord);
 

	
 
		if (order == nullptr || order->GetNonStopType() == non_stop) return;
 

	
 
		/* Keypress if negative, so 'toggle' to the next */
 
		if (non_stop < 0) {
 
			non_stop = order->GetNonStopType() ^ ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS;
 
		}
 

	
 
		this->SetWidgetDirty(WID_O_NON_STOP);
 
		DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_NON_STOP | non_stop << 4);
 
		Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_NON_STOP | non_stop << 4, {});
 
	}
 

	
 
	/**
 
	 * Handle the click on the skip button.
 
	 * If ctrl is pressed, skip to selected order, else skip to current order + 1
 
	 */
 
	void OrderClick_Skip()
 
	{
 
		/* Don't skip when there's nothing to skip */
 
		if (_ctrl_pressed && this->vehicle->cur_implicit_order_index == this->OrderGetSel()) return;
 
		if (this->vehicle->GetNumOrders() <= 1) return;
 

	
 
		DoCommandP(CMD_SKIP_TO_ORDER, _ctrl_pressed ? STR_ERROR_CAN_T_SKIP_TO_ORDER : STR_ERROR_CAN_T_SKIP_ORDER,
 
				this->vehicle->tile, this->vehicle->index, _ctrl_pressed ? this->OrderGetSel() : ((this->vehicle->cur_implicit_order_index + 1) % this->vehicle->GetNumOrders()));
 
		Command<CMD_SKIP_TO_ORDER>::Post(_ctrl_pressed ? STR_ERROR_CAN_T_SKIP_TO_ORDER : STR_ERROR_CAN_T_SKIP_ORDER,
 
				this->vehicle->tile, this->vehicle->index, _ctrl_pressed ? this->OrderGetSel() : ((this->vehicle->cur_implicit_order_index + 1) % this->vehicle->GetNumOrders()), {});
 
	}
 

	
 
	/**
 
	 * Handle the click on the delete button.
 
	 */
 
	void OrderClick_Delete()
 
	{
 
		/* When networking, move one order lower */
 
		int selected = this->selected_order + (int)_networking;
 

	
 
		if (DoCommandP(CMD_DELETE_ORDER, STR_ERROR_CAN_T_DELETE_THIS_ORDER, this->vehicle->tile, this->vehicle->index, this->OrderGetSel())) {
 
		if (Command<CMD_DELETE_ORDER>::Post(STR_ERROR_CAN_T_DELETE_THIS_ORDER, this->vehicle->tile, this->vehicle->index, this->OrderGetSel(), {})) {
 
			this->selected_order = selected >= this->vehicle->GetNumOrders() ? -1 : selected;
 
			this->UpdateButtonState();
 
		}
 
	}
 

	
 
	/**
 
	 * Handle the click on the 'stop sharing' button.
 
	 * If 'End of Shared Orders' isn't selected, do nothing. If Ctrl is pressed, call OrderClick_Delete and exit.
 
	 * To stop sharing this vehicle order list, we copy the orders of a vehicle that share this order list. That way we
 
	 * exit the group of shared vehicles while keeping the same order list.
 
	 */
 
	void OrderClick_StopSharing()
 
	{
 
		/* Don't try to stop sharing orders if 'End of Shared Orders' isn't selected. */
 
		if (!this->vehicle->IsOrderListShared() || this->selected_order != this->vehicle->GetNumOrders()) return;
 
		/* If Ctrl is pressed, delete the order list as if we clicked the 'Delete' button. */
 
		if (_ctrl_pressed) {
 
			this->OrderClick_Delete();
 
			return;
 
		}
 

	
 
		/* Get another vehicle that share orders with this vehicle. */
 
		Vehicle *other_shared = (this->vehicle->FirstShared() == this->vehicle) ? this->vehicle->NextShared() : this->vehicle->PreviousShared();
 
		/* Copy the order list of the other vehicle. */
 
		if (DoCommandP(CMD_CLONE_ORDER, STR_ERROR_CAN_T_STOP_SHARING_ORDER_LIST, this->vehicle->tile, this->vehicle->index | CO_COPY << 30, other_shared->index)) {
 
		if (Command<CMD_CLONE_ORDER>::Post(STR_ERROR_CAN_T_STOP_SHARING_ORDER_LIST, this->vehicle->tile, this->vehicle->index | CO_COPY << 30, other_shared->index, {})) {
 
			this->UpdateButtonState();
 
		}
 
	}
 

	
 
	/**
 
	 * Handle the click on the refit button.
 
	 * If ctrl is pressed, cancel refitting, else show the refit window.
 
	 * @param i Selected refit command.
 
	 * @param auto_refit Select refit for auto-refitting.
 
	 */
 
	void OrderClick_Refit(int i, bool auto_refit)
 
	{
 
		if (_ctrl_pressed) {
 
			/* Cancel refitting */
 
			DoCommandP(CMD_ORDER_REFIT, this->vehicle->tile, this->vehicle->index, (this->OrderGetSel() << 16) | (CT_NO_REFIT << 8) | CT_NO_REFIT);
 
			Command<CMD_ORDER_REFIT>::Post(this->vehicle->tile, this->vehicle->index, (this->OrderGetSel() << 16) | (CT_NO_REFIT << 8) | CT_NO_REFIT, {});
 
		} else {
 
			if (i == 1) { // Auto-refit to available cargo type.
 
				DoCommandP(CMD_ORDER_REFIT, this->vehicle->tile, this->vehicle->index, (this->OrderGetSel() << 16) | CT_AUTO_REFIT);
 
				Command<CMD_ORDER_REFIT>::Post(this->vehicle->tile, this->vehicle->index, (this->OrderGetSel() << 16) | CT_AUTO_REFIT, {});
 
			} else {
 
				ShowVehicleRefitWindow(this->vehicle, this->OrderGetSel(), this, auto_refit);
 
			}
 
		}
 
	}
 

	
 
	/** Cache auto-refittability of the vehicle chain. */
 
	void UpdateAutoRefitState()
 
	{
 
		this->can_do_refit = false;
 
		this->can_do_autorefit = false;
 
		for (const Vehicle *w = this->vehicle; w != nullptr; w = w->IsGroundVehicle() ? w->Next() : nullptr) {
 
			if (IsEngineRefittable(w->engine_type)) this->can_do_refit = true;
 
			if (HasBit(Engine::Get(w->engine_type)->info.misc_flags, EF_AUTO_REFIT)) this->can_do_autorefit = true;
 
		}
 
	}
 

	
 
public:
 
	OrdersWindow(WindowDesc *desc, const Vehicle *v) : Window(desc)
 
	{
 
		this->vehicle = v;
 

	
 
		this->CreateNestedTree();
 
		this->vscroll = this->GetScrollbar(WID_O_SCROLLBAR);
 
@@ -1138,74 +1139,74 @@ public:
 
					if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
 
					SetDParam(0, value);
 
				}
 
				break;
 
			}
 

	
 
			case WID_O_CAPTION:
 
				SetDParam(0, this->vehicle->index);
 
				break;
 
		}
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_O_ORDER_LIST: {
 
				if (this->goto_type == OPOS_CONDITIONAL) {
 
					VehicleOrderID order_id = this->GetOrderFromPt(_cursor.pos.y - this->top);
 
					if (order_id != INVALID_VEH_ORDER_ID) {
 
						Order order;
 
						order.next = nullptr;
 
						order.index = 0;
 
						order.MakeConditional(order_id);
 

	
 
						DoCommandP(CMD_INSERT_ORDER, STR_ERROR_CAN_T_INSERT_NEW_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack());
 
						Command<CMD_INSERT_ORDER>::Post(STR_ERROR_CAN_T_INSERT_NEW_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), {});
 
					}
 
					ResetObjectToPlace();
 
					break;
 
				}
 

	
 
				VehicleOrderID sel = this->GetOrderFromPt(pt.y);
 

	
 
				if (_ctrl_pressed && sel < this->vehicle->GetNumOrders()) {
 
					TileIndex xy = this->vehicle->GetOrder(sel)->GetLocation(this->vehicle);
 
					if (xy != INVALID_TILE) ScrollMainWindowToTile(xy);
 
					return;
 
				}
 

	
 
				/* This order won't be selected any more, close all child windows and dropdowns */
 
				this->CloseChildWindows();
 
				HideDropDownMenu(this);
 

	
 
				if (sel == INVALID_VEH_ORDER_ID || this->vehicle->owner != _local_company) {
 
					/* Deselect clicked order */
 
					this->selected_order = -1;
 
				} else if (sel == this->selected_order) {
 
					if (this->vehicle->type == VEH_TRAIN && sel < this->vehicle->GetNumOrders()) {
 
						DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER,
 
						Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER,
 
								this->vehicle->tile, this->vehicle->index + (sel << 20),
 
								MOF_STOP_LOCATION | ((this->vehicle->GetOrder(sel)->GetStopLocation() + 1) % OSL_END) << 4);
 
								MOF_STOP_LOCATION | ((this->vehicle->GetOrder(sel)->GetStopLocation() + 1) % OSL_END) << 4, {});
 
					}
 
				} else {
 
					/* Select clicked order */
 
					this->selected_order = sel;
 

	
 
					if (this->vehicle->owner == _local_company) {
 
						/* Activate drag and drop */
 
						SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
 
					}
 
				}
 

	
 
				this->UpdateButtonState();
 
				break;
 
			}
 

	
 
			case WID_O_SKIP:
 
				this->OrderClick_Skip();
 
				break;
 

	
 
			case WID_O_DELETE:
 
				this->OrderClick_Delete();
 
				break;
 

	
 
			case WID_O_STOP_SHARING:
 
@@ -1310,104 +1311,104 @@ public:
 
				ShowVehicleListWindow(this->vehicle);
 
				break;
 
		}
 
	}
 

	
 
	void OnQueryTextFinished(char *str) override
 
	{
 
		if (!StrEmpty(str)) {
 
			VehicleOrderID sel = this->OrderGetSel();
 
			uint value = atoi(str);
 

	
 
			switch (this->vehicle->GetOrder(sel)->GetConditionVariable()) {
 
				case OCV_MAX_SPEED:
 
					value = ConvertDisplaySpeedToSpeed(value);
 
					break;
 

	
 
				case OCV_RELIABILITY:
 
				case OCV_LOAD_PERCENTAGE:
 
					value = Clamp(value, 0, 100);
 
					break;
 

	
 
				default:
 
					break;
 
			}
 
			DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel << 20), MOF_COND_VALUE | Clamp(value, 0, 2047) << 4);
 
			Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (sel << 20), MOF_COND_VALUE | Clamp(value, 0, 2047) << 4, {});
 
		}
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		switch (widget) {
 
			case WID_O_NON_STOP:
 
				this->OrderClick_Nonstop(index);
 
				break;
 

	
 
			case WID_O_FULL_LOAD:
 
				this->OrderClick_FullLoad((OrderLoadFlags)index);
 
				break;
 

	
 
			case WID_O_UNLOAD:
 
				this->OrderClick_Unload((OrderUnloadFlags)index);
 
				break;
 

	
 
			case WID_O_GOTO:
 
				switch (index) {
 
					case 0: this->OrderClick_Goto(OPOS_GOTO); break;
 
					case 1: this->OrderClick_NearestDepot(); break;
 
					case 2: this->OrderClick_Goto(OPOS_CONDITIONAL); break;
 
					case 3: this->OrderClick_Goto(OPOS_SHARE); break;
 
					default: NOT_REACHED();
 
				}
 
				break;
 

	
 
			case WID_O_SERVICE:
 
				this->OrderClick_Service(index);
 
				break;
 

	
 
			case WID_O_REFIT_DROPDOWN:
 
				this->OrderClick_Refit(index, true);
 
				break;
 

	
 
			case WID_O_COND_VARIABLE:
 
				DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_VARIABLE | index << 4);
 
				Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_VARIABLE | index << 4, {});
 
				break;
 

	
 
			case WID_O_COND_COMPARATOR:
 
				DoCommandP(CMD_MODIFY_ORDER, STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_COMPARATOR | index << 4);
 
				Command<CMD_MODIFY_ORDER>::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_COMPARATOR | index << 4, {});
 
				break;
 
		}
 
	}
 

	
 
	void OnDragDrop(Point pt, int widget) override
 
	{
 
		switch (widget) {
 
			case WID_O_ORDER_LIST: {
 
				VehicleOrderID from_order = this->OrderGetSel();
 
				VehicleOrderID to_order = this->GetOrderFromPt(pt.y);
 

	
 
				if (!(from_order == to_order || from_order == INVALID_VEH_ORDER_ID || from_order > this->vehicle->GetNumOrders() || to_order == INVALID_VEH_ORDER_ID || to_order > this->vehicle->GetNumOrders()) &&
 
						DoCommandP(CMD_MOVE_ORDER, STR_ERROR_CAN_T_MOVE_THIS_ORDER, this->vehicle->tile, this->vehicle->index, from_order | (to_order << 16))) {
 
						Command<CMD_MOVE_ORDER>::Post(STR_ERROR_CAN_T_MOVE_THIS_ORDER, this->vehicle->tile, this->vehicle->index, from_order | (to_order << 16), {})) {
 
					this->selected_order = -1;
 
					this->UpdateButtonState();
 
				}
 
				break;
 
			}
 

	
 
			case WID_O_DELETE:
 
				this->OrderClick_Delete();
 
				break;
 

	
 
			case WID_O_STOP_SHARING:
 
				this->OrderClick_StopSharing();
 
				break;
 
		}
 

	
 
		ResetObjectToPlace();
 

	
 
		if (this->order_over != INVALID_VEH_ORDER_ID) {
 
			/* End of drag-and-drop, hide dragged order destination highlight. */
 
			this->order_over = INVALID_VEH_ORDER_ID;
 
			this->SetWidgetDirty(WID_O_ORDER_LIST);
 
		}
 
	}
 

	
 
@@ -1417,67 +1418,67 @@ public:
 

	
 
		switch (hotkey) {
 
			case OHK_SKIP:           this->OrderClick_Skip(); break;
 
			case OHK_DELETE:         this->OrderClick_Delete(); break;
 
			case OHK_GOTO:           this->OrderClick_Goto(OPOS_GOTO); break;
 
			case OHK_NONSTOP:        this->OrderClick_Nonstop(-1); break;
 
			case OHK_FULLLOAD:       this->OrderClick_FullLoad(OLF_FULL_LOAD_ANY, true); break;
 
			case OHK_UNLOAD:         this->OrderClick_Unload(OUFB_UNLOAD, true); break;
 
			case OHK_NEAREST_DEPOT:  this->OrderClick_NearestDepot(); break;
 
			case OHK_ALWAYS_SERVICE: this->OrderClick_Service(-1); break;
 
			case OHK_TRANSFER:       this->OrderClick_Unload(OUFB_TRANSFER, true); break;
 
			case OHK_NO_UNLOAD:      this->OrderClick_Unload(OUFB_NO_UNLOAD, true); break;
 
			case OHK_NO_LOAD:        this->OrderClick_FullLoad(OLFB_NO_LOAD, true); break;
 
			default: return ES_NOT_HANDLED;
 
		}
 
		return ES_HANDLED;
 
	}
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		if (this->goto_type == OPOS_GOTO) {
 
			const Order cmd = GetOrderCmdFromTile(this->vehicle, tile);
 
			if (cmd.IsType(OT_NOTHING)) return;
 

	
 
			if (DoCommandP(CMD_INSERT_ORDER, STR_ERROR_CAN_T_INSERT_NEW_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), cmd.Pack())) {
 
			if (Command<CMD_INSERT_ORDER>::Post(STR_ERROR_CAN_T_INSERT_NEW_ORDER, this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), cmd.Pack(), {})) {
 
				/* With quick goto the Go To button stays active */
 
				if (!_settings_client.gui.quick_goto) ResetObjectToPlace();
 
			}
 
		}
 
	}
 

	
 
	bool OnVehicleSelect(const Vehicle *v) override
 
	{
 
		/* v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet.
 
		 * We disallow copying orders of other vehicles if we already have at least one order entry
 
		 * ourself as it easily copies orders of vehicles within a station when we mean the station.
 
		 * Obviously if you press CTRL on a non-empty orders vehicle you know what you are doing
 
		 * TODO: give a warning message */
 
		bool share_order = _ctrl_pressed || this->goto_type == OPOS_SHARE;
 
		if (this->vehicle->GetNumOrders() != 0 && !share_order) return false;
 

	
 
		if (DoCommandP(CMD_CLONE_ORDER, share_order ? STR_ERROR_CAN_T_SHARE_ORDER_LIST : STR_ERROR_CAN_T_COPY_ORDER_LIST,
 
				this->vehicle->tile, this->vehicle->index | (share_order ? CO_SHARE : CO_COPY) << 30, v->index)) {
 
		if (Command<CMD_CLONE_ORDER>::Post(share_order ? STR_ERROR_CAN_T_SHARE_ORDER_LIST : STR_ERROR_CAN_T_COPY_ORDER_LIST,
 
				this->vehicle->tile, this->vehicle->index | (share_order ? CO_SHARE : CO_COPY) << 30, v->index, {})) {
 
			this->selected_order = -1;
 
			ResetObjectToPlace();
 
		}
 
		return true;
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		this->goto_type = OPOS_NONE;
 
		this->SetWidgetDirty(WID_O_GOTO);
 

	
 
		/* Remove drag highlighting if it exists. */
 
		if (this->order_over != INVALID_VEH_ORDER_ID) {
 
			this->order_over = INVALID_VEH_ORDER_ID;
 
			this->SetWidgetDirty(WID_O_ORDER_LIST);
 
		}
 
	}
 

	
 
	void OnMouseDrag(Point pt, int widget) override
 
	{
 
		if (this->selected_order != -1 && widget == WID_O_ORDER_LIST) {
 
			/* An order is dragged.. */
 
			VehicleOrderID from_order = this->OrderGetSel();
 
			VehicleOrderID to_order = this->GetOrderFromPt(pt.y);
src/rail_gui.cpp
Show inline comments
 
@@ -18,48 +18,49 @@
 
#include "newgrf_station.h"
 
#include "company_base.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "date_func.h"
 
#include "sound_func.h"
 
#include "company_func.h"
 
#include "widgets/dropdown_type.h"
 
#include "tunnelbridge.h"
 
#include "tilehighlight_func.h"
 
#include "spritecache.h"
 
#include "core/geometry_func.hpp"
 
#include "hotkeys.h"
 
#include "engine_base.h"
 
#include "vehicle_func.h"
 
#include "zoom_func.h"
 
#include "rail_gui.h"
 
#include "querystring_gui.h"
 
#include "sortlist_type.h"
 
#include "stringfilter_type.h"
 
#include "string_func.h"
 
#include "station_cmd.h"
 
#include "tunnelbridge_cmd.h"
 
#include "waypoint_cmd.h"
 
#include "rail_cmd.h"
 

	
 
#include "station_map.h"
 
#include "tunnelbridge_map.h"
 

	
 
#include "widgets/rail_widget.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
static RailType _cur_railtype;               ///< Rail type of the current build-rail toolbar.
 
static bool _remove_button_clicked;          ///< Flag whether 'remove' toggle-button is currently enabled
 
static DiagDirection _build_depot_direction; ///< Currently selected depot direction
 
static byte _waypoint_count = 1;             ///< Number of waypoint types
 
static byte _cur_waypoint_type;              ///< Currently selected waypoint type
 
static bool _convert_signal_button;          ///< convert signal button in the signal GUI pressed
 
static SignalVariant _cur_signal_variant;    ///< set the signal variant (for signal GUI)
 
static SignalType _cur_signal_type;          ///< set the signal type (for signal GUI)
 

	
 
struct RailStationGUISettings {
 
	Axis orientation;                 ///< Currently selected rail station orientation
 

	
 
	bool newstations;                 ///< Are custom station definitions available?
 
	StationClassID station_class;     ///< Currently selected custom station class (if newstations is \c true )
 
	byte station_type;                ///< %Station type within the currently selected custom station class (if newstations is \c true )
 
@@ -74,68 +75,71 @@ static void ShowBuildWaypointPicker(Wind
 
static Window *ShowStationBuilder(Window *parent);
 
static void ShowSignalBuilder(Window *parent);
 

	
 
/**
 
 * Check whether a station type can be build.
 
 * @return true if building is allowed.
 
 */
 
static bool IsStationAvailable(const StationSpec *statspec)
 
{
 
	if (statspec == nullptr || !HasBit(statspec->callback_mask, CBM_STATION_AVAIL)) return true;
 

	
 
	uint16 cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, nullptr, INVALID_TILE);
 
	if (cb_res == CALLBACK_FAILED) return true;
 

	
 
	return Convert8bitBooleanCallback(statspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res);
 
}
 

	
 
void CcPlaySound_CONSTRUCTION_RAIL(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile);
 
}
 

	
 
static void GenericPlaceRail(TileIndex tile, int cmd)
 
{
 
	DoCommandP(_remove_button_clicked ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL,
 
			_remove_button_clicked ? STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK : STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK,
 
			CcPlaySound_CONSTRUCTION_RAIL,
 
			tile, _cur_railtype, cmd | (_settings_client.gui.auto_remove_signals << 3));
 
	if (_remove_button_clicked) {
 
		Command<CMD_REMOVE_SINGLE_RAIL>::Post(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK, CcPlaySound_CONSTRUCTION_RAIL,
 
				tile, _cur_railtype, cmd | (_settings_client.gui.auto_remove_signals << 3), {});
 
	} else {
 
		Command<CMD_BUILD_SINGLE_RAIL>::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK, CcPlaySound_CONSTRUCTION_RAIL,
 
				tile, _cur_railtype, cmd | (_settings_client.gui.auto_remove_signals << 3), {});
 
	}
 
}
 

	
 
/**
 
 * Try to add an additional rail-track at the entrance of a depot
 
 * @param tile  Tile to use for adding the rail-track
 
 * @param dir   Direction to check for already present tracks
 
 * @param track Track to add
 
 * @see CcRailDepot()
 
 */
 
static void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track)
 
{
 
	if (GetRailTileType(tile) == RAIL_TILE_DEPOT) return;
 
	if (GetRailTileType(tile) == RAIL_TILE_SIGNALS && !_settings_client.gui.auto_remove_signals) return;
 
	if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return;
 

	
 
	DoCommandP(CMD_BUILD_SINGLE_RAIL, tile, _cur_railtype, track | (_settings_client.gui.auto_remove_signals << 3));
 
	Command<CMD_BUILD_SINGLE_RAIL>::Post(tile, _cur_railtype, track | (_settings_client.gui.auto_remove_signals << 3), {});
 
}
 

	
 
/** Additional pieces of track to add at the entrance of a depot. */
 
static const Track _place_depot_extra_track[12] = {
 
	TRACK_LEFT,  TRACK_UPPER, TRACK_UPPER, TRACK_RIGHT, // First additional track for directions 0..3
 
	TRACK_X,     TRACK_Y,     TRACK_X,     TRACK_Y,     // Second additional track
 
	TRACK_LOWER, TRACK_LEFT,  TRACK_RIGHT, TRACK_LOWER, // Third additional track
 
};
 

	
 
/** Direction to check for existing track pieces. */
 
static const DiagDirection _place_depot_extra_dir[12] = {
 
	DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, DIAGDIR_SW,
 
	DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_SE,
 
	DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE,
 
};
 

	
 
void CcRailDepot(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Failed()) return;
 

	
 
	DiagDirection dir = (DiagDirection)p2;
 

	
 
	if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile);
 
	if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
@@ -147,149 +151,149 @@ void CcRailDepot(const CommandCost &resu
 
		PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]);
 
		PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]);
 
	}
 
}
 

	
 
/**
 
 * Place a rail waypoint.
 
 * @param tile Position to start dragging a waypoint.
 
 */
 
static void PlaceRail_Waypoint(TileIndex tile)
 
{
 
	if (_remove_button_clicked) {
 
		VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_STATION);
 
		return;
 
	}
 

	
 
	Axis axis = GetAxisForNewWaypoint(tile);
 
	if (IsValidAxis(axis)) {
 
		/* Valid tile for waypoints */
 
		VpStartPlaceSizing(tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_STATION);
 
		VpSetPlaceSizingLimit(_settings_game.station.station_spread);
 
	} else {
 
		/* Tile where we can't build rail waypoints. This is always going to fail,
 
		 * but provides the user with a proper error message. */
 
		DoCommandP(CMD_BUILD_RAIL_WAYPOINT, STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, tile, 1 << 8 | 1 << 16, STAT_CLASS_WAYP | INVALID_STATION << 16);
 
		Command<CMD_BUILD_RAIL_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, tile, 1 << 8 | 1 << 16, STAT_CLASS_WAYP | INVALID_STATION << 16, {});
 
	}
 
}
 

	
 
void CcStation(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Failed()) return;
 

	
 
	if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile);
 
	/* Only close the station builder window if the default station and non persistent building is chosen. */
 
	if (_railstation.station_class == STAT_CLASS_DFLT && _railstation.station_type == 0 && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
}
 

	
 
/**
 
 * Place a rail station.
 
 * @param tile Position to place or start dragging a station.
 
 */
 
static void PlaceRail_Station(TileIndex tile)
 
{
 
	if (_remove_button_clicked) {
 
		VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_REMOVE_STATION);
 
		VpSetPlaceSizingLimit(-1);
 
	} else if (_settings_client.gui.station_dragdrop) {
 
		VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION);
 
		VpSetPlaceSizingLimit(_settings_game.station.station_spread);
 
	} else {
 
		uint32 p1 = _cur_railtype | _railstation.orientation << 6 | _settings_client.gui.station_numtracks << 8 | _settings_client.gui.station_platlength << 16 | _ctrl_pressed << 24;
 
		uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16;
 

	
 
		int w = _settings_client.gui.station_numtracks;
 
		int h = _settings_client.gui.station_platlength;
 
		if (!_railstation.orientation) Swap(w, h);
 

	
 
		auto proc = [=](bool test, StationID to_join) -> bool {
 
			if (test) {
 
				return Command<CMD_BUILD_RAIL_STATION>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_RAIL_STATION>()), tile, p1, p2, {}).Succeeded();
 
			} else {
 
				uint32 p2_final = p2;
 
				if (to_join != INVALID_STATION) SB(p2_final, 16, 16, to_join);
 

	
 
				return DoCommandP(CMD_BUILD_RAIL_STATION, STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, tile, p1, p2_final);
 
				return Command<CMD_BUILD_RAIL_STATION>::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, tile, p1, p2_final, {});
 
			}
 
		};
 

	
 
		ShowSelectStationIfNeeded(TileArea(tile, w, h), proc);
 
	}
 
}
 

	
 
/**
 
 * Build a new signal or edit/remove a present signal, use CmdBuildSingleSignal() or CmdRemoveSingleSignal() in rail_cmd.cpp
 
 *
 
 * @param tile The tile where the signal will build or edit
 
 */
 
static void GenericPlaceSignals(TileIndex tile)
 
{
 
	TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0));
 

	
 
	if (trackbits & TRACK_BIT_VERT) { // N-S direction
 
		trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT;
 
	}
 

	
 
	if (trackbits & TRACK_BIT_HORZ) { // E-W direction
 
		trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER;
 
	}
 

	
 
	Track track = FindFirstTrack(trackbits);
 

	
 
	if (_remove_button_clicked) {
 
		DoCommandP(CMD_REMOVE_SIGNALS, STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM, CcPlaySound_CONSTRUCTION_RAIL,  tile, track, 0);
 
		Command<CMD_REMOVE_SIGNALS>::Post(STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM, CcPlaySound_CONSTRUCTION_RAIL, tile, track, 0, {});
 
	} else {
 
		const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
 

	
 
		/* various bitstuffed elements for CmdBuildSingleSignal() */
 
		uint32 p1 = track;
 

	
 
		/* Which signals should we cycle through? */
 
		uint8 cycle_types;
 

	
 
		if (_settings_client.gui.cycle_signal_types == SIGNAL_CYCLE_ALL && _settings_client.gui.signal_gui_mode == SIGNAL_GUI_ALL) {
 
			cycle_types = SIGTYPE_NORMAL | (SIGTYPE_LAST << 3);
 
		} else {
 
			cycle_types = SIGTYPE_PBS | (SIGTYPE_LAST << 3);
 
		}
 

	
 
		if (w != nullptr) {
 
			/* signal GUI is used */
 
			SB(p1, 3, 1, _ctrl_pressed);
 
			SB(p1, 4, 1, _cur_signal_variant);
 
			SB(p1, 5, 3, _cur_signal_type);
 
			SB(p1, 8, 1, _convert_signal_button);
 
			SB(p1, 9, 6, cycle_types);
 
		} else {
 
			SB(p1, 3, 1, _ctrl_pressed);
 
			SB(p1, 4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC));
 
			SB(p1, 5, 3, SIGTYPE_PBS_ONEWAY);
 
			SB(p1, 8, 1, 0);
 
			SB(p1, 9, 6, cycle_types);
 
		}
 

	
 
		DoCommandP(CMD_BUILD_SIGNALS, (w != nullptr && _convert_signal_button) ? STR_ERROR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE : STR_ERROR_CAN_T_BUILD_SIGNALS_HERE,
 
				CcPlaySound_CONSTRUCTION_RAIL, tile, p1, 0);
 
		Command<CMD_BUILD_SIGNALS>::Post((w != nullptr && _convert_signal_button) ? STR_ERROR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE : STR_ERROR_CAN_T_BUILD_SIGNALS_HERE,
 
				CcPlaySound_CONSTRUCTION_RAIL, tile, p1, 0, {});
 
	}
 
}
 

	
 
/**
 
 * Start placing a rail bridge.
 
 * @param tile Position of the first tile of the bridge.
 
 * @param w    Rail toolbar window.
 
 */
 
static void PlaceRail_Bridge(TileIndex tile, Window *w)
 
{
 
	if (IsBridgeTile(tile)) {
 
		TileIndex other_tile = GetOtherTunnelBridgeEnd(tile);
 
		Point pt = {0, 0};
 
		w->OnPlaceMouseUp(VPM_X_OR_Y, DDSP_BUILD_BRIDGE, pt, other_tile, tile);
 
	} else {
 
		VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_BUILD_BRIDGE);
 
	}
 
}
 

	
 
/** Command callback for building a tunnel */
 
void CcBuildRailTunnel(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Succeeded()) {
 
		if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile);
 
@@ -349,52 +353,55 @@ static void BuildRailClick_Remove(Window
 
		if (_remove_button_clicked) {
 
			/* starting drag & drop remove */
 
			if (!_settings_client.gui.station_dragdrop) {
 
				SetTileSelectSize(1, 1);
 
			} else {
 
				VpSetPlaceSizingLimit(-1);
 
			}
 
		} else {
 
			/* starting station build mode */
 
			if (!_settings_client.gui.station_dragdrop) {
 
				int x = _settings_client.gui.station_numtracks;
 
				int y = _settings_client.gui.station_platlength;
 
				if (_railstation.orientation == 0) Swap(x, y);
 
				SetTileSelectSize(x, y);
 
			} else {
 
				VpSetPlaceSizingLimit(_settings_game.station.station_spread);
 
			}
 
		}
 
	}
 
}
 

	
 
static void DoRailroadTrack(int mode)
 
{
 
	uint32 p2 = _cur_railtype | (mode << 6) | (_settings_client.gui.auto_remove_signals << 11);
 
	DoCommandP(_remove_button_clicked ? CMD_REMOVE_RAILROAD_TRACK : CMD_BUILD_RAILROAD_TRACK,
 
			_remove_button_clicked ? STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK : STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK,
 
			CcPlaySound_CONSTRUCTION_RAIL,
 
			TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), p2);
 
	if (_remove_button_clicked) {
 
		Command<CMD_REMOVE_RAILROAD_TRACK>::Post(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK, CcPlaySound_CONSTRUCTION_RAIL,
 
				TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), p2, {});
 
	} else {
 
		Command<CMD_BUILD_RAILROAD_TRACK>::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK, CcPlaySound_CONSTRUCTION_RAIL,
 
				TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), p2, {});
 
	}
 
}
 

	
 
static void HandleAutodirPlacement()
 
{
 
	int trackstat = _thd.drawstyle & HT_DIR_MASK; // 0..5
 

	
 
	if (_thd.drawstyle & HT_RAIL) { // one tile case
 
		GenericPlaceRail(TileVirtXY(_thd.selend.x, _thd.selend.y), trackstat);
 
		return;
 
	}
 

	
 
	DoRailroadTrack(trackstat);
 
}
 

	
 
/**
 
 * Build new signals or remove signals or (if only one tile marked) edit a signal.
 
 *
 
 * If one tile marked abort and use GenericPlaceSignals()
 
 * else use CmdBuildSingleSignal() or CmdRemoveSingleSignal() in rail_cmd.cpp to build many signals
 
 */
 
static void HandleAutoSignalPlacement()
 
{
 
	uint32 p2 = GB(_thd.drawstyle, 0, 3); // 0..5
 

	
 
@@ -403,52 +410,55 @@ static void HandleAutoSignalPlacement()
 
		return;
 
	}
 

	
 
	const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
 

	
 
	if (w != nullptr) {
 
		/* signal GUI is used */
 
		SB(p2,  3, 1, 0);
 
		SB(p2,  4, 1, _cur_signal_variant);
 
		SB(p2,  6, 1, _ctrl_pressed);
 
		SB(p2,  7, 3, _cur_signal_type);
 
		SB(p2, 24, 8, _settings_client.gui.drag_signals_density);
 
		SB(p2, 10, 1, !_settings_client.gui.drag_signals_fixed_distance);
 
	} else {
 
		SB(p2,  3, 1, 0);
 
		SB(p2,  4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC));
 
		SB(p2,  6, 1, _ctrl_pressed);
 
		SB(p2,  7, 3, SIGTYPE_PBS_ONEWAY);
 
		SB(p2, 24, 8, _settings_client.gui.drag_signals_density);
 
		SB(p2, 10, 1, !_settings_client.gui.drag_signals_fixed_distance);
 
	}
 

	
 
	/* _settings_client.gui.drag_signals_density is given as a parameter such that each user
 
	 * in a network game can specify their own signal density */
 
	DoCommandP(_remove_button_clicked ? CMD_REMOVE_SIGNAL_TRACK : CMD_BUILD_SIGNAL_TRACK,
 
			_remove_button_clicked ? STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM : STR_ERROR_CAN_T_BUILD_SIGNALS_HERE,
 
			CcPlaySound_CONSTRUCTION_RAIL,
 
			TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), p2);
 
	if (_remove_button_clicked) {
 
		Command<CMD_REMOVE_SIGNAL_TRACK>::Post(STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM, CcPlaySound_CONSTRUCTION_RAIL,
 
				TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), p2, {});
 
	} else {
 
		Command<CMD_BUILD_SIGNAL_TRACK>::Post(STR_ERROR_CAN_T_BUILD_SIGNALS_HERE, CcPlaySound_CONSTRUCTION_RAIL,
 
				TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), p2, {});
 
	}
 
}
 

	
 

	
 
/** Rail toolbar management class. */
 
struct BuildRailToolbarWindow : Window {
 
	RailType railtype;    ///< Rail type to build.
 
	int last_user_action; ///< Last started user action.
 

	
 
	BuildRailToolbarWindow(WindowDesc *desc, RailType railtype) : Window(desc)
 
	{
 
		this->InitNested(TRANSPORT_RAIL);
 
		this->SetupRailToolbar(railtype);
 
		this->DisableWidget(WID_RAT_REMOVE);
 
		this->last_user_action = WIDGET_LIST_END;
 

	
 
		if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this);
 
	}
 

	
 
	void Close() override
 
	{
 
		if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true);
 
		if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
 
		this->Window::Close();
 
	}
 
@@ -632,140 +642,139 @@ struct BuildRailToolbarWindow : Window {
 
				VpStartPlaceSizing(tile, VPM_FIX_VERTICAL | VPM_RAILDIRS, DDSP_PLACE_RAIL);
 
				break;
 

	
 
			case WID_RAT_BUILD_X:
 
				VpStartPlaceSizing(tile, VPM_FIX_Y | VPM_RAILDIRS, DDSP_PLACE_RAIL);
 
				break;
 

	
 
			case WID_RAT_BUILD_EW:
 
				VpStartPlaceSizing(tile, VPM_FIX_HORIZONTAL | VPM_RAILDIRS, DDSP_PLACE_RAIL);
 
				break;
 

	
 
			case WID_RAT_BUILD_Y:
 
				VpStartPlaceSizing(tile, VPM_FIX_X | VPM_RAILDIRS, DDSP_PLACE_RAIL);
 
				break;
 

	
 
			case WID_RAT_AUTORAIL:
 
				VpStartPlaceSizing(tile, VPM_RAILDIRS, DDSP_PLACE_RAIL);
 
				break;
 

	
 
			case WID_RAT_DEMOLISH:
 
				PlaceProc_DemolishArea(tile);
 
				break;
 

	
 
			case WID_RAT_BUILD_DEPOT:
 
				DoCommandP(CMD_BUILD_TRAIN_DEPOT, STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT,
 
						CcRailDepot, tile, _cur_railtype, _build_depot_direction);
 
				Command<CMD_BUILD_TRAIN_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, tile, _cur_railtype, _build_depot_direction, {});
 
				break;
 

	
 
			case WID_RAT_BUILD_WAYPOINT:
 
				PlaceRail_Waypoint(tile);
 
				break;
 

	
 
			case WID_RAT_BUILD_STATION:
 
				PlaceRail_Station(tile);
 
				break;
 

	
 
			case WID_RAT_BUILD_SIGNALS:
 
				VpStartPlaceSizing(tile, VPM_SIGNALDIRS, DDSP_BUILD_SIGNALS);
 
				break;
 

	
 
			case WID_RAT_BUILD_BRIDGE:
 
				PlaceRail_Bridge(tile, this);
 
				break;
 

	
 
			case WID_RAT_BUILD_TUNNEL:
 
				DoCommandP(CMD_BUILD_TUNNEL, STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRailTunnel, tile, _cur_railtype | (TRANSPORT_RAIL << 8), 0);
 
				Command<CMD_BUILD_TUNNEL>::Post(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRailTunnel, tile, _cur_railtype | (TRANSPORT_RAIL << 8), 0, {});
 
				break;
 

	
 
			case WID_RAT_CONVERT_RAIL:
 
				VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_RAIL);
 
				break;
 

	
 
			default: NOT_REACHED();
 
		}
 
	}
 

	
 
	void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) override
 
	{
 
		/* no dragging if you have pressed the convert button */
 
		if (FindWindowById(WC_BUILD_SIGNAL, 0) != nullptr && _convert_signal_button && this->IsWidgetLowered(WID_RAT_BUILD_SIGNALS)) return;
 

	
 
		VpSelectTilesWithMethod(pt.x, pt.y, select_method);
 
	}
 

	
 
	void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) override
 
	{
 
		if (pt.x != -1) {
 
			switch (select_proc) {
 
				default: NOT_REACHED();
 
				case DDSP_BUILD_BRIDGE:
 
					if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
					ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_RAIL, _cur_railtype);
 
					break;
 

	
 
				case DDSP_PLACE_RAIL:
 
					HandleAutodirPlacement();
 
					break;
 

	
 
				case DDSP_BUILD_SIGNALS:
 
					HandleAutoSignalPlacement();
 
					break;
 

	
 
				case DDSP_DEMOLISH_AREA:
 
					GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
 
					break;
 

	
 
				case DDSP_CONVERT_RAIL:
 
					DoCommandP(CMD_CONVERT_RAIL, STR_ERROR_CAN_T_CONVERT_RAIL, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _cur_railtype | (_ctrl_pressed ? 1 << 6 : 0));
 
					Command<CMD_CONVERT_RAIL>::Post(STR_ERROR_CAN_T_CONVERT_RAIL, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _cur_railtype | (_ctrl_pressed ? 1 << 6 : 0), {});
 
					break;
 

	
 
				case DDSP_REMOVE_STATION:
 
				case DDSP_BUILD_STATION:
 
					if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) {
 
						/* Station */
 
						if (_remove_button_clicked) {
 
							DoCommandP(CMD_REMOVE_FROM_RAIL_STATION, STR_ERROR_CAN_T_REMOVE_PART_OF_STATION, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _ctrl_pressed ? 0 : 1);
 
							Command<CMD_REMOVE_FROM_RAIL_STATION>::Post(STR_ERROR_CAN_T_REMOVE_PART_OF_STATION, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _ctrl_pressed ? 0 : 1, {});
 
						} else {
 
							HandleStationPlacement(start_tile, end_tile);
 
						}
 
					} else {
 
						/* Waypoint */
 
						if (_remove_button_clicked) {
 
							DoCommandP(CMD_REMOVE_FROM_RAIL_WAYPOINT, STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _ctrl_pressed ? 0 : 1);
 
							Command<CMD_REMOVE_FROM_RAIL_WAYPOINT>::Post(STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _ctrl_pressed ? 0 : 1, {});
 
						} else {
 
							TileArea ta(start_tile, end_tile);
 
							uint32 p1 = _cur_railtype | (select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y) << 6 | ta.w << 8 | ta.h << 16 | _ctrl_pressed << 24;
 
							uint32 p2 = STAT_CLASS_WAYP | _cur_waypoint_type << 8 | INVALID_STATION << 16;
 

	
 
							auto proc = [=](bool test, StationID to_join) -> bool {
 
								if (test) {
 
									return Command<CMD_BUILD_RAIL_WAYPOINT>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_RAIL_WAYPOINT>()), ta.tile, p1, p2, {}).Succeeded();
 
								} else {
 
									uint32 p2_final = p2;
 
									if (to_join != INVALID_STATION) SB(p2_final, 16, 16, to_join);
 

	
 
									return DoCommandP(CMD_BUILD_RAIL_WAYPOINT, STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, ta.tile, p1, p2_final);
 
									return Command<CMD_BUILD_RAIL_WAYPOINT>::Post(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, ta.tile, p1, p2_final, {});
 
								}
 
							};
 

	
 
							ShowSelectWaypointIfNeeded(ta, proc);
 
						}
 
					}
 
					break;
 
			}
 
		}
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true);
 

	
 
		this->RaiseButtons();
 
		this->DisableWidget(WID_RAT_REMOVE);
 
		this->SetWidgetDirty(WID_RAT_REMOVE);
 

	
 
		CloseWindowById(WC_BUILD_SIGNAL, TRANSPORT_RAIL);
 
		CloseWindowById(WC_BUILD_STATION, TRANSPORT_RAIL);
 
		CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_RAIL);
 
		CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_RAIL);
 
		CloseWindowById(WC_SELECT_STATION, 0);
 
@@ -892,49 +901,49 @@ Window *ShowBuildRailToolbar(RailType ra
 
	return new BuildRailToolbarWindow(&_build_rail_desc, railtype);
 
}
 

	
 
/* TODO: For custom stations, respect their allowed platforms/lengths bitmasks!
 
 * --pasky */
 

	
 
static void HandleStationPlacement(TileIndex start, TileIndex end)
 
{
 
	TileArea ta(start, end);
 
	uint numtracks = ta.w;
 
	uint platlength = ta.h;
 

	
 
	if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength);
 

	
 
	uint32 p1 = _cur_railtype | _railstation.orientation << 6 | numtracks << 8 | platlength << 16 | _ctrl_pressed << 24;
 
	uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16;
 

	
 
	auto proc = [=](bool test, StationID to_join) -> bool {
 
		if (test) {
 
			return Command<CMD_BUILD_RAIL_STATION>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_RAIL_STATION>()), ta.tile, p1, p2, {}).Succeeded();
 
		} else {
 
			uint32 p2_final = p2;
 
			if (to_join != INVALID_STATION) SB(p2_final, 16, 16, to_join);
 

	
 
			return DoCommandP(CMD_BUILD_RAIL_STATION, STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, ta.tile, p1, p2_final);
 
			return Command<CMD_BUILD_RAIL_STATION>::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, ta.tile, p1, p2_final, {});
 
		}
 
	};
 

	
 
	ShowSelectStationIfNeeded(ta, proc);
 
}
 

	
 
/** Enum referring to the Hotkeys in the build rail station window */
 
enum BuildRalStationHotkeys {
 
	BRASHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
 
};
 

	
 
struct BuildRailStationWindow : public PickerWindowBase {
 
private:
 
	uint line_height;     ///< Height of a single line in the newstation selection matrix (#WID_BRAS_NEWST_LIST widget).
 
	uint coverage_height; ///< Height of the coverage texts.
 
	Scrollbar *vscroll;   ///< Vertical scrollbar of the new station list.
 
	Scrollbar *vscroll2;  ///< Vertical scrollbar of the matrix with new stations.
 

	
 
	typedef GUIList<StationClassID, StringFilter &> GUIStationClassList; ///< Type definition for the list to hold available station classes.
 

	
 
	static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.
 

	
 
	static Listing   last_sorting;           ///< Default sorting of #GUIStationClassList.
 
	static Filtering last_filtering;         ///< Default filtering of #GUIStationClassList.
src/road_gui.cpp
Show inline comments
 
@@ -11,48 +11,49 @@
 
#include "gui.h"
 
#include "window_gui.h"
 
#include "station_gui.h"
 
#include "terraform_gui.h"
 
#include "viewport_func.h"
 
#include "command_func.h"
 
#include "road_cmd.h"
 
#include "station_func.h"
 
#include "window_func.h"
 
#include "vehicle_func.h"
 
#include "sound_func.h"
 
#include "company_func.h"
 
#include "tunnelbridge.h"
 
#include "tunnelbridge_map.h"
 
#include "tilehighlight_func.h"
 
#include "company_base.h"
 
#include "hotkeys.h"
 
#include "road_gui.h"
 
#include "zoom_func.h"
 
#include "engine_base.h"
 
#include "strings_func.h"
 
#include "core/geometry_func.hpp"
 
#include "date_func.h"
 
#include "station_cmd.h"
 
#include "road_cmd.h"
 
#include "tunnelbridge_cmd.h"
 

	
 
#include "widgets/road_widget.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
static void ShowRVStationPicker(Window *parent, RoadStopType rs);
 
static void ShowRoadDepotPicker(Window *parent);
 

	
 
static bool _remove_button_clicked;
 
static bool _one_way_button_clicked;
 

	
 
/**
 
 * Define the values of the RoadFlags
 
 * @see CmdBuildLongRoad
 
 */
 
enum RoadFlags {
 
	RF_NONE             = 0x00,
 
	RF_START_HALFROAD_Y = 0x01,    // The start tile in Y-dir should have only a half road
 
	RF_END_HALFROAD_Y   = 0x02,    // The end tile in Y-dir should have only a half road
 
	RF_DIR_Y            = 0x04,    // The direction is Y-dir
 
	RF_DIR_X            = RF_NONE, // Dummy; Dir X is set when RF_DIR_Y is not set
 
@@ -106,49 +107,49 @@ void CcBuildRoadTunnel(const CommandCost
 
		if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 

	
 
		DiagDirection start_direction = ReverseDiagDir(GetTunnelBridgeDirection(start_tile));
 
		ConnectRoadToStructure(start_tile, start_direction);
 

	
 
		TileIndex end_tile = GetOtherTunnelBridgeEnd(start_tile);
 
		DiagDirection end_direction = ReverseDiagDir(GetTunnelBridgeDirection(end_tile));
 
		ConnectRoadToStructure(end_tile, end_direction);
 
	} else {
 
		SetRedErrorSquare(_build_tunnel_endtile);
 
	}
 
}
 

	
 
/**
 
 * If required, connects a new structure to an existing road or tram by building the missing roadbit.
 
 * @param tile Tile containing the structure to connect.
 
 * @param direction Direction to check.
 
 */
 
void ConnectRoadToStructure(TileIndex tile, DiagDirection direction)
 
{
 
	tile += TileOffsByDiagDir(direction);
 
	/* if there is a roadpiece just outside of the station entrance, build a connecting route */
 
	if (IsNormalRoadTile(tile)) {
 
		if (GetRoadBits(tile, GetRoadTramType(_cur_roadtype)) != ROAD_NONE) {
 
			DoCommandP(CMD_BUILD_ROAD, tile, _cur_roadtype << 4 | DiagDirToRoadBits(ReverseDiagDir(direction)), 0);
 
			Command<CMD_BUILD_ROAD>::Post(tile, _cur_roadtype << 4 | DiagDirToRoadBits(ReverseDiagDir(direction)), 0, {});
 
		}
 
	}
 
}
 

	
 
void CcRoadDepot(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Failed()) return;
 

	
 
	DiagDirection dir = (DiagDirection)GB(p1, 0, 2);
 
	if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
 
	if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
	ConnectRoadToStructure(tile, dir);
 
}
 

	
 
/**
 
 * Command callback for building road stops.
 
 * @param result Result of the build road stop command.
 
 * @param cmd Unused.
 
 * @param tile Start tile.
 
 * @param p1 bit 0..7: Width of the road stop.
 
 *           bit 8..15: Length of the road stop.
 
 * @param p2 bit 0: 0 For bus stops, 1 for truck stops.
 
 *           bit 1: 0 For normal stops, 1 for drive-through.
 
 *           bit 2: Allow stations directly adjacent to other stations.
 
@@ -184,49 +185,49 @@ void CcRoadStop(const CommandCost &resul
 
 * @param err_msg Error message to show.
 
 * @see CcRoadStop()
 
 */
 
static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, uint32 p2, StringID err_msg)
 
{
 
	TileArea ta(start_tile, end_tile);
 
	uint32 p1 = (uint32)(ta.w | ta.h << 8);
 

	
 
	uint8 ddir = _road_station_picker_orientation;
 
	SB(p2, 16, 16, INVALID_STATION); // no station to join
 

	
 
	if (ddir >= DIAGDIR_END) {
 
		SetBit(p2, 1); // It's a drive-through stop.
 
		ddir -= DIAGDIR_END; // Adjust picker result to actual direction.
 
	}
 
	p2 |= ddir << 3; // Set the DiagDirecion into p2 bits 3 and 4.
 

	
 
	auto proc = [=](bool test, StationID to_join) -> bool {
 
		if (test) {
 
			return Command<CMD_BUILD_ROAD_STOP>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_ROAD_STOP>()), ta.tile, p1, p2, {}).Succeeded();
 
		} else {
 
			uint32 p2_final = p2;
 
			if (to_join != INVALID_STATION) SB(p2_final, 16, 16, to_join);
 

	
 
			return DoCommandP(CMD_BUILD_ROAD_STOP, err_msg, CcRoadStop, ta.tile, p1, p2_final);
 
			return Command<CMD_BUILD_ROAD_STOP>::Post(err_msg, CcRoadStop, ta.tile, p1, p2_final, {});
 
		}
 
	};
 

	
 
	ShowSelectStationIfNeeded(ta, proc);
 
}
 

	
 
/**
 
 * Callback for placing a bus station.
 
 * @param tile Position to place the station.
 
 */
 
static void PlaceRoad_BusStation(TileIndex tile)
 
{
 
	if (_remove_button_clicked) {
 
		VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP);
 
	} else {
 
		if (_road_station_picker_orientation < DIAGDIR_END) { // Not a drive-through stop.
 
			VpStartPlaceSizing(tile, (DiagDirToAxis(_road_station_picker_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP);
 
		} else {
 
			VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP);
 
		}
 
		VpSetPlaceSizingLimit(_settings_game.station.station_spread);
 
	}
 
}
 

	
 
@@ -545,67 +546,67 @@ struct BuildRoadToolbarWindow : Window {
 
			case WID_ROT_ROAD_X:
 
				_place_road_flag = RF_DIR_X;
 
				if (_tile_fract_coords.x >= 8) _place_road_flag |= RF_START_HALFROAD_X;
 
				VpStartPlaceSizing(tile, VPM_FIX_Y, DDSP_PLACE_ROAD_X_DIR);
 
				break;
 

	
 
			case WID_ROT_ROAD_Y:
 
				_place_road_flag = RF_DIR_Y;
 
				if (_tile_fract_coords.y >= 8) _place_road_flag |= RF_START_HALFROAD_Y;
 
				VpStartPlaceSizing(tile, VPM_FIX_X, DDSP_PLACE_ROAD_Y_DIR);
 
				break;
 

	
 
			case WID_ROT_AUTOROAD:
 
				_place_road_flag = RF_NONE;
 
				if (_tile_fract_coords.x >= 8) _place_road_flag |= RF_START_HALFROAD_X;
 
				if (_tile_fract_coords.y >= 8) _place_road_flag |= RF_START_HALFROAD_Y;
 
				VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_PLACE_AUTOROAD);
 
				break;
 

	
 
			case WID_ROT_DEMOLISH:
 
				PlaceProc_DemolishArea(tile);
 
				break;
 

	
 
			case WID_ROT_DEPOT:
 
				DoCommandP(CMD_BUILD_ROAD_DEPOT, this->rti->strings.err_depot, CcRoadDepot,
 
						tile, _cur_roadtype << 2 | _road_depot_orientation, 0);
 
				Command<CMD_BUILD_ROAD_DEPOT>::Post(this->rti->strings.err_depot, CcRoadDepot,
 
						tile, _cur_roadtype << 2 | _road_depot_orientation, 0, {});
 
				break;
 

	
 
			case WID_ROT_BUS_STATION:
 
				PlaceRoad_BusStation(tile);
 
				break;
 

	
 
			case WID_ROT_TRUCK_STATION:
 
				PlaceRoad_TruckStation(tile);
 
				break;
 

	
 
			case WID_ROT_BUILD_BRIDGE:
 
				PlaceRoad_Bridge(tile, this);
 
				break;
 

	
 
			case WID_ROT_BUILD_TUNNEL:
 
				DoCommandP(CMD_BUILD_TUNNEL, STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRoadTunnel,
 
						tile, _cur_roadtype | (TRANSPORT_ROAD << 8), 0);
 
				Command<CMD_BUILD_TUNNEL>::Post(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRoadTunnel,
 
						tile, _cur_roadtype | (TRANSPORT_ROAD << 8), 0, {});
 
				break;
 

	
 
			case WID_ROT_CONVERT_ROAD:
 
				VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_ROAD);
 
				break;
 

	
 
			default: NOT_REACHED();
 
		}
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true);
 

	
 
		this->RaiseButtons();
 
		this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
 
		this->SetWidgetDirty(WID_ROT_REMOVE);
 

	
 
		if (RoadTypeIsRoad(this->roadtype)) {
 
			this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true);
 
			this->SetWidgetDirty(WID_ROT_ONE_WAY);
 
		}
 

	
 
		CloseWindowById(WC_BUS_STATION, TRANSPORT_ROAD);
 
@@ -664,79 +665,83 @@ struct BuildRoadToolbarWindow : Window {
 
			switch (select_proc) {
 
				default: NOT_REACHED();
 
				case DDSP_BUILD_BRIDGE:
 
					if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
 
					ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, _cur_roadtype);
 
					break;
 

	
 
				case DDSP_DEMOLISH_AREA:
 
					GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
 
					break;
 

	
 
				case DDSP_PLACE_ROAD_X_DIR:
 
				case DDSP_PLACE_ROAD_Y_DIR:
 
				case DDSP_PLACE_AUTOROAD:
 
					/* Flag description:
 
					 * Use the first three bits (0x07) if dir == Y
 
					 * else use the last 2 bits (X dir has
 
					 * not the 3rd bit set) */
 

	
 
					/* Even if _cur_roadtype_id is a uint8 we only use 5 bits so
 
					 * we could ignore the last 3 bits and reuse them for other
 
					 * flags */
 
					_place_road_flag = (RoadFlags)((_place_road_flag & RF_DIR_Y) ? (_place_road_flag & 0x07) : (_place_road_flag >> 3));
 

	
 
					DoCommandP(_remove_button_clicked ? CMD_REMOVE_LONG_ROAD : CMD_BUILD_LONG_ROAD,
 
							_remove_button_clicked ? this->rti->strings.err_remove_road : this->rti->strings.err_build_road, CcPlaySound_CONSTRUCTION_OTHER,
 
							start_tile, end_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 10));
 
					if (_remove_button_clicked) {
 
						Command<CMD_REMOVE_LONG_ROAD>::Post(this->rti->strings.err_remove_road, CcPlaySound_CONSTRUCTION_OTHER,
 
								start_tile, end_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 10), {});
 
					} else {
 
						Command<CMD_BUILD_LONG_ROAD>::Post(this->rti->strings.err_build_road, CcPlaySound_CONSTRUCTION_OTHER,
 
								start_tile, end_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 10), {});
 
					}
 
					break;
 

	
 
				case DDSP_BUILD_BUSSTOP:
 
				case DDSP_REMOVE_BUSSTOP:
 
					if (this->IsWidgetLowered(WID_ROT_BUS_STATION)) {
 
						if (_remove_button_clicked) {
 
							TileArea ta(start_tile, end_tile);
 
							DoCommandP(CMD_REMOVE_ROAD_STOP, this->rti->strings.err_remove_station[ROADSTOP_BUS], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS);
 
							Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_BUS], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS, {});
 
						} else {
 
							PlaceRoadStop(start_tile, end_tile, _cur_roadtype << 5 | (_ctrl_pressed << 2) | ROADSTOP_BUS, this->rti->strings.err_build_station[ROADSTOP_BUS]);
 
						}
 
					}
 
					break;
 

	
 
				case DDSP_BUILD_TRUCKSTOP:
 
				case DDSP_REMOVE_TRUCKSTOP:
 
					if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION)) {
 
						if (_remove_button_clicked) {
 
							TileArea ta(start_tile, end_tile);
 
							DoCommandP(CMD_REMOVE_ROAD_STOP, this->rti->strings.err_remove_station[ROADSTOP_TRUCK], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK);
 
							Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_TRUCK], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK, {});
 
						} else {
 
							PlaceRoadStop(start_tile, end_tile, _cur_roadtype << 5 | (_ctrl_pressed << 2) | ROADSTOP_TRUCK, this->rti->strings.err_build_station[ROADSTOP_TRUCK]);
 
						}
 
					}
 
					break;
 

	
 
				case DDSP_CONVERT_ROAD:
 
					DoCommandP(CMD_CONVERT_ROAD, rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype);
 
					Command<CMD_CONVERT_ROAD>::Post(rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype, {});
 
					break;
 
			}
 
		}
 
	}
 

	
 
	void OnPlacePresize(Point pt, TileIndex tile) override
 
	{
 
		Command<CMD_BUILD_TUNNEL>::Do(DC_AUTO, tile, _cur_roadtype | (TRANSPORT_ROAD << 8), 0, {});
 
		VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile);
 
	}
 

	
 
	EventState OnCTRLStateChange() override
 
	{
 
		if (RoadToolbar_CtrlChanged(this)) return ES_HANDLED;
 
		return ES_NOT_HANDLED;
 
	}
 

	
 
	static HotkeyList road_hotkeys;
 
	static HotkeyList tram_hotkeys;
 
};
 

	
 
/**
 
 * Handler for global hotkeys of the BuildRoadToolbarWindow.
 
 * @param hotkey Hotkey
src/settings.cpp
Show inline comments
 
@@ -1529,75 +1529,75 @@ CommandCost CmdChangeCompanySetting(DoCo
 
	const SettingDesc *sd = GetCompanySettingFromName(text.c_str());
 

	
 
	if (sd == nullptr) return CMD_ERROR;
 
	if (!sd->IsIntSetting()) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		sd->AsIntSetting()->ChangeValue(&Company::Get(_current_company)->settings, p2);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Top function to save the new value of an element of the Settings struct
 
 * @param index offset in the SettingDesc array of the Settings struct which
 
 * identifies the setting member we want to change
 
 * @param value new value of the setting
 
 * @param force_newgame force the newgame settings
 
 */
 
bool SetSettingValue(const IntSettingDesc *sd, int32 value, bool force_newgame)
 
{
 
	const IntSettingDesc *setting = sd->AsIntSetting();
 
	if ((setting->flags & SF_PER_COMPANY) != 0) {
 
		if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) {
 
			return DoCommandP(CMD_CHANGE_COMPANY_SETTING, 0, 0, value, setting->GetName());
 
			return Command<CMD_CHANGE_COMPANY_SETTING>::Post(0, 0, value, setting->GetName());
 
		}
 

	
 
		setting->ChangeValue(&_settings_client.company, value);
 
		return true;
 
	}
 

	
 
	/* If an item is company-based, we do not send it over the network
 
	 * (if any) to change. Also *hack*hack* we update the _newgame version
 
	 * of settings because changing a company-based setting in a game also
 
	 * changes its defaults. At least that is the convention we have chosen */
 
	if (setting->flags & SF_NO_NETWORK_SYNC) {
 
		if (_game_mode != GM_MENU) {
 
			setting->ChangeValue(&_settings_newgame, value);
 
		}
 
		setting->ChangeValue(&GetGameSettings(), value);
 
		return true;
 
	}
 

	
 
	if (force_newgame) {
 
		setting->ChangeValue(&_settings_newgame, value);
 
		return true;
 
	}
 

	
 
	/* send non-company-based settings over the network */
 
	if (!_networking || (_networking && _network_server)) {
 
		return DoCommandP(CMD_CHANGE_SETTING, 0, 0, value, setting->GetName());
 
		return Command<CMD_CHANGE_SETTING>::Post(0, 0, value, setting->GetName());
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Set the company settings for a new company to their default values.
 
 */
 
void SetDefaultCompanySettings(CompanyID cid)
 
{
 
	Company *c = Company::Get(cid);
 
	for (auto &desc : _company_settings) {
 
		const IntSettingDesc *int_setting = GetSettingDesc(desc)->AsIntSetting();
 
		int_setting->MakeValueValidAndWrite(&c->settings, int_setting->def);
 
	}
 
}
 

	
 
/**
 
 * Sync all company settings in a multiplayer game.
 
 */
 
void SyncCompanySettings()
 
{
 
	const void *old_object = &Company::Get(_current_company)->settings;
 
	const void *new_object = &_settings_client.company;
 
	for (auto &desc : _company_settings) {
src/signs_cmd.cpp
Show inline comments
 
@@ -111,26 +111,26 @@ CommandCost CmdRenameSign(DoCommandFlag 
 
 * Callback function that is called after a sign is placed
 
 * @param result of the operation
 
 * @param cmd unused
 
 * @param tile unused
 
 * @param p1 unused
 
 * @param p2 unused
 
 * @param text unused
 
 */
 
void CcPlaceSign(const CommandCost &result, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
 
{
 
	if (result.Failed()) return;
 

	
 
	ShowRenameSignWindow(Sign::Get(_new_sign_id));
 
	ResetObjectToPlace();
 
}
 

	
 
/**
 
 *
 
 * PlaceProc function, called when someone pressed the button if the
 
 *  sign-tool is selected
 
 * @param tile on which to place the sign
 
 */
 
void PlaceProc_Sign(TileIndex tile)
 
{
 
	DoCommandP(CMD_PLACE_SIGN, STR_ERROR_CAN_T_PLACE_SIGN_HERE, CcPlaceSign, tile, 0, 0);
 
	Command<CMD_PLACE_SIGN>::Post(STR_ERROR_CAN_T_PLACE_SIGN_HERE, CcPlaceSign, tile, 0, 0, {});
 
}
src/signs_gui.cpp
Show inline comments
 
@@ -5,48 +5,49 @@
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file signs_gui.cpp The GUI for signs. */
 

	
 
#include "stdafx.h"
 
#include "company_gui.h"
 
#include "company_func.h"
 
#include "signs_base.h"
 
#include "signs_func.h"
 
#include "debug.h"
 
#include "command_func.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "map_func.h"
 
#include "viewport_func.h"
 
#include "querystring_gui.h"
 
#include "sortlist_type.h"
 
#include "stringfilter_type.h"
 
#include "string_func.h"
 
#include "core/geometry_func.hpp"
 
#include "hotkeys.h"
 
#include "transparency.h"
 
#include "gui.h"
 
#include "signs_cmd.h"
 

	
 
#include "widgets/sign_widget.h"
 

	
 
#include "table/strings.h"
 
#include "table/sprites.h"
 

	
 
#include "safeguards.h"
 

	
 
struct SignList {
 
	/**
 
	 * A GUIList contains signs and uses a StringFilter for filtering.
 
	 */
 
	typedef GUIList<const Sign *, StringFilter &> GUISignList;
 

	
 
	GUISignList signs;
 

	
 
	StringFilter string_filter;                                       ///< The match string to be used when the GUIList is (re)-sorted.
 
	static bool match_case;                                           ///< Should case sensitive matching be used?
 
	static char default_name[64];                                     ///< Default sign name, used if Sign::name is nullptr.
 

	
 
	/**
 
	 * Creates a SignList with filtering disabled by default.
 
	 */
 
	SignList() : string_filter(&match_case)
 
@@ -392,49 +393,49 @@ static WindowDesc _sign_list_desc(
 
	0,
 
	_nested_sign_list_widgets, lengthof(_nested_sign_list_widgets),
 
	&SignListWindow::hotkeys
 
);
 

	
 
/**
 
 * Open the sign list window
 
 *
 
 * @return newly opened sign list window, or nullptr if the window could not be opened.
 
 */
 
Window *ShowSignList()
 
{
 
	return AllocateWindowDescFront<SignListWindow>(&_sign_list_desc, 0);
 
}
 

	
 
/**
 
 * Actually rename the sign.
 
 * @param index the sign to rename.
 
 * @param text  the new name.
 
 * @return true if the window will already be removed after returning.
 
 */
 
static bool RenameSign(SignID index, const char *text)
 
{
 
	bool remove = StrEmpty(text);
 
	DoCommandP(CMD_RENAME_SIGN, StrEmpty(text) ? STR_ERROR_CAN_T_DELETE_SIGN : STR_ERROR_CAN_T_CHANGE_SIGN_NAME, 0, index, 0, text);
 
	Command<CMD_RENAME_SIGN>::Post(StrEmpty(text) ? STR_ERROR_CAN_T_DELETE_SIGN : STR_ERROR_CAN_T_CHANGE_SIGN_NAME, 0, index, 0, text);
 
	return remove;
 
}
 

	
 
struct SignWindow : Window, SignList {
 
	QueryString name_editbox;
 
	SignID cur_sign;
 

	
 
	SignWindow(WindowDesc *desc, const Sign *si) : Window(desc), name_editbox(MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_SIGN_NAME_CHARS)
 
	{
 
		this->querystrings[WID_QES_TEXT] = &this->name_editbox;
 
		this->name_editbox.caption = STR_EDIT_SIGN_CAPTION;
 
		this->name_editbox.cancel_button = WID_QES_CANCEL;
 
		this->name_editbox.ok_button = WID_QES_OK;
 

	
 
		this->InitNested(WN_QUERY_STRING_SIGN);
 

	
 
		UpdateSignEditWindow(si);
 
		this->SetFocusedWidget(WID_QES_TEXT);
 
	}
 

	
 
	void UpdateSignEditWindow(const Sign *si)
 
	{
 
		/* Display an empty string when the sign hasn't been edited yet */
 
		if (!si->name.empty()) {
src/station_gui.cpp
Show inline comments
 
@@ -10,48 +10,49 @@
 
#include "stdafx.h"
 
#include "debug.h"
 
#include "gui.h"
 
#include "textbuf_gui.h"
 
#include "company_func.h"
 
#include "command_func.h"
 
#include "vehicle_gui.h"
 
#include "cargotype.h"
 
#include "station_gui.h"
 
#include "strings_func.h"
 
#include "string_func.h"
 
#include "window_func.h"
 
#include "viewport_func.h"
 
#include "widgets/dropdown_func.h"
 
#include "station_base.h"
 
#include "waypoint_base.h"
 
#include "tilehighlight_func.h"
 
#include "company_base.h"
 
#include "sortlist_type.h"
 
#include "core/geometry_func.hpp"
 
#include "vehiclelist.h"
 
#include "town.h"
 
#include "linkgraph/linkgraph.h"
 
#include "zoom_func.h"
 
#include "station_cmd.h"
 

	
 
#include "widgets/station_widget.h"
 

	
 
#include "table/strings.h"
 

	
 
#include <set>
 
#include <vector>
 

	
 
#include "safeguards.h"
 

	
 
/**
 
 * Calculates and draws the accepted or supplied cargo around the selected tile(s)
 
 * @param left x position where the string is to be drawn
 
 * @param right the right most position to draw on
 
 * @param top y position where the string is to be drawn
 
 * @param sct which type of cargo is to be displayed (passengers/non-passengers)
 
 * @param rad radius around selected tile(s) to be searched
 
 * @param supplies if supplied cargoes should be drawn, else accepted cargoes
 
 * @return Returns the y value below the string that was drawn
 
 */
 
int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
 
{
 
	TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
 
	CargoTypes cargo_mask = 0;
 
@@ -1926,49 +1927,49 @@ struct StationViewWindow : public Window
 
				break;
 

	
 
			case WID_SV_ACCEPTS_RATINGS: {
 
				/* Swap between 'accepts' and 'ratings' view. */
 
				int height_change;
 
				NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS);
 
				if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
 
					nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP); // Switch to accepts view.
 
					height_change = this->rating_lines - this->accepts_lines;
 
				} else {
 
					nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP); // Switch to ratings view.
 
					height_change = this->accepts_lines - this->rating_lines;
 
				}
 
				this->ReInit(0, height_change * FONT_HEIGHT_NORMAL);
 
				break;
 
			}
 

	
 
			case WID_SV_RENAME:
 
				SetDParam(0, this->window_number);
 
				ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_CHARS,
 
						this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
 
				break;
 

	
 
			case WID_SV_CLOSE_AIRPORT:
 
				DoCommandP(CMD_OPEN_CLOSE_AIRPORT, 0, this->window_number, 0);
 
				Command<CMD_OPEN_CLOSE_AIRPORT>::Post(0, this->window_number, 0, {});
 
				break;
 

	
 
			case WID_SV_TRAINS:   // Show list of scheduled trains to this station
 
			case WID_SV_ROADVEHS: // Show list of scheduled road-vehicles to this station
 
			case WID_SV_SHIPS:    // Show list of scheduled ships to this station
 
			case WID_SV_PLANES: { // Show list of scheduled aircraft to this station
 
				Owner owner = Station::Get(this->window_number)->owner;
 
				ShowVehicleListWindow(owner, (VehicleType)(widget - WID_SV_TRAINS), (StationID)this->window_number);
 
				break;
 
			}
 

	
 
			case WID_SV_SORT_BY: {
 
				/* The initial selection is composed of current mode and
 
				 * sorting criteria for columns 1, 2, and 3. Column 0 is always
 
				 * sorted by cargo ID. The others can theoretically be sorted
 
				 * by different things but there is no UI for that. */
 
				ShowDropDownMenu(this, _sort_names,
 
						this->current_mode * 2 + (this->sortings[1] == ST_COUNT ? 1 : 0),
 
						WID_SV_SORT_BY, 0, 0);
 
				break;
 
			}
 

	
 
			case WID_SV_GROUP_BY: {
 
				ShowDropDownMenu(this, _group_names, this->grouping_index, WID_SV_GROUP_BY, 0, 0);
 
@@ -2063,49 +2064,49 @@ struct StationViewWindow : public Window
 
				this->groupings[3] = GR_NEXT;
 
				break;
 
			case STR_STATION_VIEW_GROUP_D_V_S:
 
				this->groupings[1] = GR_DESTINATION;
 
				this->groupings[2] = GR_NEXT;
 
				this->groupings[3] = GR_SOURCE;
 
				break;
 
		}
 
		this->SetDirty();
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		if (widget == WID_SV_SORT_BY) {
 
			this->SelectSortBy(index);
 
		} else {
 
			this->SelectGroupBy(index);
 
		}
 
	}
 

	
 
	void OnQueryTextFinished(char *str) override
 
	{
 
		if (str == nullptr) return;
 

	
 
		DoCommandP(CMD_RENAME_STATION, STR_ERROR_CAN_T_RENAME_STATION, 0, this->window_number, 0, str);
 
		Command<CMD_RENAME_STATION>::Post(STR_ERROR_CAN_T_RENAME_STATION, 0, this->window_number, 0, str);
 
	}
 

	
 
	void OnResize() override
 
	{
 
		this->vscroll->SetCapacityFromWidget(this, WID_SV_WAITING, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
 
	}
 

	
 
	/**
 
	 * Some data on this window has become invalid. Invalidate the cache for the given cargo if necessary.
 
	 * @param data Information about the changed data. If it's a valid cargo ID, invalidate the cargo data.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	void OnInvalidateData(int data = 0, bool gui_scope = true) override
 
	{
 
		if (gui_scope) {
 
			if (data >= 0 && data < NUM_CARGO) {
 
				this->cached_destinations.Remove((CargoID)data);
 
			} else {
 
				this->ReInit();
 
			}
 
		}
 
	}
 
};
 

	
src/story_gui.cpp
Show inline comments
 
@@ -4,48 +4,49 @@
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file story_gui.cpp GUI for stories. */
 

	
 
#include "stdafx.h"
 
#include "window_gui.h"
 
#include "strings_func.h"
 
#include "date_func.h"
 
#include "gui.h"
 
#include "story_base.h"
 
#include "core/geometry_func.hpp"
 
#include "company_func.h"
 
#include "command_func.h"
 
#include "widgets/dropdown_type.h"
 
#include "widgets/dropdown_func.h"
 
#include "sortlist_type.h"
 
#include "goal_base.h"
 
#include "viewport_func.h"
 
#include "window_func.h"
 
#include "company_base.h"
 
#include "tilehighlight_func.h"
 
#include "vehicle_base.h"
 
#include "story_cmd.h"
 

	
 
#include "widgets/story_widget.h"
 

	
 
#include "table/strings.h"
 
#include "table/sprites.h"
 

	
 
#include <numeric>
 

	
 
#include "safeguards.h"
 

	
 
static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor);
 

	
 
typedef GUIList<const StoryPage*> GUIStoryPageList;
 
typedef GUIList<const StoryPageElement*> GUIStoryPageElementList;
 

	
 
struct StoryBookWindow : Window {
 
protected:
 
	struct LayoutCacheElement {
 
		const StoryPageElement *pe;
 
		Rect bounds;
 
	};
 
	typedef std::vector<LayoutCacheElement> LayoutCache;
 

	
 
	enum class ElementFloat {
 
@@ -545,49 +546,49 @@ protected:
 
	{
 
		switch (pe.type) {
 
			case SPET_TEXT:
 
				/* Do nothing. */
 
				break;
 

	
 
			case SPET_LOCATION:
 
				if (_ctrl_pressed) {
 
					ShowExtraViewportWindow((TileIndex)pe.referenced_id);
 
				} else {
 
					ScrollMainWindowToTile((TileIndex)pe.referenced_id);
 
				}
 
				break;
 

	
 
			case SPET_GOAL:
 
				ShowGoalsList((CompanyID)this->window_number);
 
				break;
 

	
 
			case SPET_BUTTON_PUSH:
 
				if (this->active_button_id != INVALID_STORY_PAGE_ELEMENT) ResetObjectToPlace();
 
				this->active_button_id = pe.index;
 
				this->SetTimeout();
 
				this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 

	
 
				DoCommandP(CMD_STORY_PAGE_BUTTON, 0, pe.index, 0);
 
				Command<CMD_STORY_PAGE_BUTTON>::Post(0, pe.index, 0, {});
 
				break;
 

	
 
			case SPET_BUTTON_TILE:
 
				if (this->active_button_id == pe.index) {
 
					ResetObjectToPlace();
 
					this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
				} else {
 
					CursorID cursor = TranslateStoryPageButtonCursor(StoryPageButtonData{ pe.referenced_id }.GetCursor());
 
					SetObjectToPlaceWnd(cursor, PAL_NONE, HT_RECT, this);
 
					this->active_button_id = pe.index;
 
				}
 
				this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
				break;
 

	
 
			case SPET_BUTTON_VEHICLE:
 
				if (this->active_button_id == pe.index) {
 
					ResetObjectToPlace();
 
					this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
				} else {
 
					CursorID cursor = TranslateStoryPageButtonCursor(StoryPageButtonData{ pe.referenced_id }.GetCursor());
 
					SetObjectToPlaceWnd(cursor, PAL_NONE, HT_VEHICLE, this);
 
					this->active_button_id = pe.index;
 
				}
 
				this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
@@ -900,68 +901,68 @@ public:
 
			this->SetWidgetDisabledState(WID_SB_SEL_PAGE, this->story_pages.size() == 0);
 
			this->SetWidgetDirty(WID_SB_SEL_PAGE);
 
			this->UpdatePrevNextDisabledState();
 
		} else if (data >= 0 && this->selected_page_id == data) {
 
			this->RefreshSelectedPage();
 
		}
 
	}
 

	
 
	void OnTimeout() override
 
	{
 
		this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
		this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
	}
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
 
		if (pe == nullptr || pe->type != SPET_BUTTON_TILE) {
 
			ResetObjectToPlace();
 
			this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
			this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
			return;
 
		}
 

	
 
		DoCommandP(CMD_STORY_PAGE_BUTTON, tile, pe->index, 0);
 
		Command<CMD_STORY_PAGE_BUTTON>::Post(tile, pe->index, 0, {});
 
		ResetObjectToPlace();
 
	}
 

	
 
	bool OnVehicleSelect(const Vehicle *v) override
 
	{
 
		const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id);
 
		if (pe == nullptr || pe->type != SPET_BUTTON_VEHICLE) {
 
			ResetObjectToPlace();
 
			this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
			this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
			return false;
 
		}
 

	
 
		/* Check that the vehicle matches the requested type */
 
		StoryPageButtonData data{ pe->referenced_id };
 
		VehicleType wanted_vehtype = data.GetVehicleType();
 
		if (wanted_vehtype != VEH_INVALID && wanted_vehtype != v->type) return false;
 

	
 
		DoCommandP(CMD_STORY_PAGE_BUTTON, 0, pe->index, v->index);
 
		Command<CMD_STORY_PAGE_BUTTON>::Post(0, pe->index, v->index, {});
 
		ResetObjectToPlace();
 
		return true;
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		this->active_button_id = INVALID_STORY_PAGE_ELEMENT;
 
		this->SetWidgetDirty(WID_SB_PAGE_PANEL);
 
	}
 
};
 

	
 
GUIStoryPageList::SortFunction * const StoryBookWindow::page_sorter_funcs[] = {
 
	&PageOrderSorter,
 
};
 

	
 
GUIStoryPageElementList::SortFunction * const StoryBookWindow::page_element_sorter_funcs[] = {
 
	&PageElementOrderSorter,
 
};
 

	
 
static const NWidgetPart _nested_story_book_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
 
		NWidget(WWT_CAPTION, COLOUR_BROWN, WID_SB_CAPTION), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_SHADEBOX, COLOUR_BROWN),

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)