Changeset - r26124:7d51c03a3f5e
[Not reviewed]
master
0 23 0
Michael Lutz - 3 years ago 2021-11-30 21:59:23
michi@icosahedron.de
Codechange: Don't use globals for story/goal/sign/group command proc return values.
23 files changed with 105 insertions and 236 deletions:
0 comments (0 inline, 0 general)
src/autoreplace_cmd.cpp
Show inline comments
 
@@ -384,49 +384,49 @@ static inline CommandCost DoCmdStartStop
 
 * @param after The vehicle to insert 'v' after, or nullptr to start new chain
 
 * @param flags the command flags to use
 
 * @param whole_chain move all vehicles following 'v' (true), or only 'v' (false)
 
 * @return success or error
 
 */
 
static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlag flags, bool whole_chain)
 
{
 
	return Command<CMD_MOVE_RAIL_VEHICLE>::Do(flags | DC_NO_CARGO_CAP_CHECK, v->index, after != nullptr ? after->index : INVALID_VEHICLE, whole_chain);
 
}
 

	
 
/**
 
 * Copy head specific things to the new vehicle chain after it was successfully constructed
 
 * @param old_head The old front vehicle (no wagons attached anymore)
 
 * @param new_head The new head of the completely replaced vehicle chain
 
 * @param flags the command flags to use
 
 */
 
static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
 
{
 
	CommandCost cost = CommandCost();
 

	
 
	/* Share orders */
 
	if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command<CMD_CLONE_ORDER>::Do(DC_EXEC, CO_SHARE, new_head->index, old_head->index));
 

	
 
	/* Copy group membership */
 
	if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command<CMD_ADD_VEHICLE_GROUP>::Do(DC_EXEC, old_head->group_id, new_head->index, false));
 
	if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command<CMD_ADD_VEHICLE_GROUP>::Do(DC_EXEC, old_head->group_id, new_head->index, false)));
 

	
 
	/* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */
 
	if (cost.Succeeded()) {
 
		/* Start the vehicle, might be denied by certain things */
 
		assert((new_head->vehstatus & VS_STOPPED) != 0);
 
		cost.AddCost(DoCmdStartStopVehicle(new_head, true));
 

	
 
		/* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */
 
		if (cost.Succeeded()) cost.AddCost(DoCmdStartStopVehicle(new_head, false));
 
	}
 

	
 
	/* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */
 
	if (cost.Succeeded() && old_head != new_head && (flags & DC_EXEC) != 0) {
 
		/* Copy other things which cannot be copied by a command and which shall not stay resetted from the build vehicle command */
 
		new_head->CopyVehicleConfigAndStatistics(old_head);
 

	
 
		/* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */
 
		ChangeVehicleViewports(old_head->index, new_head->index);
 
		ChangeVehicleViewWindow(old_head->index, new_head->index);
 
		ChangeVehicleNews(old_head->index, new_head->index);
 
	}
 

	
 
	return cost;
 
}
src/goal.cpp
Show inline comments
 
@@ -7,121 +7,119 @@
 

	
 
/** @file goal.cpp Handling of goals. */
 

	
 
#include "stdafx.h"
 
#include "company_func.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "window_func.h"
 
#include "goal_base.h"
 
#include "core/pool_func.hpp"
 
#include "game/game.hpp"
 
#include "command_func.h"
 
#include "company_base.h"
 
#include "story_base.h"
 
#include "string_func.h"
 
#include "gui.h"
 
#include "network/network.h"
 
#include "network/network_base.h"
 
#include "network/network_func.h"
 
#include "goal_cmd.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
GoalID _new_goal_id;
 

	
 
GoalPool _goal_pool("Goal");
 
INSTANTIATE_POOL_METHODS(Goal)
 

	
 
/**
 
 * Create a new goal.
 
 * @param flags type of operation
 
 * @param company Company for which this goal is.
 
 * @param type GoalType of destination.
 
 * @param dest GoalTypeID of destination.
 
 * @param text Text of the goal.
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdCreateGoal(DoCommandFlag flags, CompanyID company, GoalType type, GoalTypeID dest, const std::string &text)
 
std::tuple<CommandCost, GoalID> CmdCreateGoal(DoCommandFlag flags, CompanyID company, GoalType type, GoalTypeID dest, const std::string &text)
 
{
 
	if (!Goal::CanAllocateItem()) return CMD_ERROR;
 
	if (!Goal::CanAllocateItem()) return { CMD_ERROR, INVALID_GOAL };
 

	
 
	if (_current_company != OWNER_DEITY) return CMD_ERROR;
 
	if (text.empty()) return CMD_ERROR;
 
	if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
 
	if (_current_company != OWNER_DEITY) return { CMD_ERROR, INVALID_GOAL };
 
	if (text.empty()) return { CMD_ERROR, INVALID_GOAL };
 
	if (company != INVALID_COMPANY && !Company::IsValidID(company)) return { CMD_ERROR, INVALID_GOAL };
 

	
 
	switch (type) {
 
		case GT_NONE:
 
			if (dest != 0) return CMD_ERROR;
 
			if (dest != 0) return { CMD_ERROR, INVALID_GOAL };
 
			break;
 

	
 
		case GT_TILE:
 
			if (!IsValidTile(dest)) return CMD_ERROR;
 
			if (!IsValidTile(dest)) return { CMD_ERROR, INVALID_GOAL };
 
			break;
 

	
 
		case GT_INDUSTRY:
 
			if (!Industry::IsValidID(dest)) return CMD_ERROR;
 
			if (!Industry::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL };
 
			break;
 

	
 
		case GT_TOWN:
 
			if (!Town::IsValidID(dest)) return CMD_ERROR;
 
			if (!Town::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL };
 
			break;
 

	
 
		case GT_COMPANY:
 
			if (!Company::IsValidID(dest)) return CMD_ERROR;
 
			if (!Company::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL };
 
			break;
 

	
 
		case GT_STORY_PAGE: {
 
			if (!StoryPage::IsValidID(dest)) return CMD_ERROR;
 
			if (!StoryPage::IsValidID(dest)) return { CMD_ERROR, INVALID_GOAL };
 
			CompanyID story_company = StoryPage::Get(dest)->company;
 
			if (company == INVALID_COMPANY ? story_company != INVALID_COMPANY : story_company != INVALID_COMPANY && story_company != company) return CMD_ERROR;
 
			if (company == INVALID_COMPANY ? story_company != INVALID_COMPANY : story_company != INVALID_COMPANY && story_company != company) return { CMD_ERROR, INVALID_GOAL };
 
			break;
 
		}
 

	
 
		default: return CMD_ERROR;
 
		default: return { CMD_ERROR, INVALID_GOAL };
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		Goal *g = new Goal();
 
		g->type = type;
 
		g->dst = dest;
 
		g->company = company;
 
		g->text = stredup(text.c_str());
 
		g->progress = nullptr;
 
		g->completed = false;
 

	
 
		if (g->company == INVALID_COMPANY) {
 
			InvalidateWindowClassesData(WC_GOALS_LIST);
 
		} else {
 
			InvalidateWindowData(WC_GOALS_LIST, g->company);
 
		}
 
		if (Goal::GetNumItems() == 1) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
 

	
 
		_new_goal_id = g->index;
 
		return { CommandCost(), g->index };
 
	}
 

	
 
	return CommandCost();
 
	return { CommandCost(), INVALID_GOAL };
 
}
 

	
 
/**
 
 * Remove a goal.
 
 * @param flags type of operation
 
 * @param goal GoalID to remove.
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdRemoveGoal(DoCommandFlag flags, GoalID goal)
 
{
 
	if (_current_company != OWNER_DEITY) return CMD_ERROR;
 
	if (!Goal::IsValidID(goal)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		Goal *g = Goal::Get(goal);
 
		CompanyID c = g->company;
 
		delete g;
 

	
 
		if (c == INVALID_COMPANY) {
 
			InvalidateWindowClassesData(WC_GOALS_LIST);
 
		} else {
 
			InvalidateWindowData(WC_GOALS_LIST, c);
 
		}
 
		if (Goal::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
src/goal_cmd.h
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 goal_cmd.h Command definitions related to goals. */
 

	
 
#ifndef GOAL_CMD_H
 
#define GOAL_CMD_H
 

	
 
#include "command_type.h"
 
#include "command_type.h"
 
#include "goal_type.h"
 

	
 
CommandCost CmdCreateGoal(DoCommandFlag flags, CompanyID company, GoalType type, GoalTypeID dest, const std::string &text);
 
std::tuple<CommandCost, GoalID> CmdCreateGoal(DoCommandFlag flags, CompanyID company, GoalType type, GoalTypeID dest, const std::string &text);
 
CommandCost CmdRemoveGoal(DoCommandFlag flags, GoalID goal);
 
CommandCost CmdSetGoalText(DoCommandFlag flags, GoalID goal, const std::string &text);
 
CommandCost CmdSetGoalProgress(DoCommandFlag flags, GoalID goal, const std::string &text);
 
CommandCost CmdSetGoalCompleted(DoCommandFlag flags, GoalID goal, bool completed);
 
CommandCost CmdGoalQuestion(DoCommandFlag flags, uint16 uniqueid, uint16 target, bool is_client, uint32 button_mask, GoalQuestionType type, const std::string &text);
 
CommandCost CmdGoalQuestionAnswer(DoCommandFlag flags, uint16 uniqueid, uint8 button);
 

	
 
DEF_CMD_TRAIT(CMD_CREATE_GOAL,          CmdCreateGoal,         CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_REMOVE_GOAL,          CmdRemoveGoal,         CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SET_GOAL_TEXT,        CmdSetGoalText,        CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SET_GOAL_PROGRESS,    CmdSetGoalProgress,    CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SET_GOAL_COMPLETED,   CmdSetGoalCompleted,   CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_GOAL_QUESTION,        CmdGoalQuestion,       CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_GOAL_QUESTION_ANSWER, CmdGoalQuestionAnswer, CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 

	
 
#endif /* GOAL_CMD_H */
src/goal_type.h
Show inline comments
 
@@ -16,28 +16,27 @@ static const uint32 GOAL_QUESTION_BUTTON
 

	
 
enum GoalQuestionType : byte {
 
	GQT_QUESTION = 0,
 
	GQT_INFORMATION = 1,
 
	GQT_WARNING = 2,
 
	GQT_ERROR = 3,
 
	GQT_END = 4,
 
};
 

	
 
/** Types of goal destinations */
 
enum GoalType : byte {
 
	GT_NONE,         ///< Destination is not linked
 
	GT_TILE,         ///< Destination is a tile
 
	GT_INDUSTRY,     ///< Destination is an industry
 
	GT_TOWN,         ///< Destination is a town
 
	GT_COMPANY,      ///< Destination is a company
 
	GT_STORY_PAGE,   ///< Destination is a story page
 
};
 

	
 
typedef uint32 GoalTypeID; ///< Contains either tile, industry ID, town ID or company ID (or INVALID_GOALTYPE)
 
static const GoalTypeID INVALID_GOALTYPE = 0xFFFFFFFF; ///< Invalid/unknown index of GoalType
 

	
 
typedef uint16 GoalID; ///< ID of a goal
 
struct Goal;
 

	
 
extern GoalID _new_goal_id;
 
static const GoalID INVALID_GOAL = 0xFFFF; ///< Constant representing a non-existing goal.
 

	
 
#endif /* GOAL_TYPE_H */
src/group.h
Show inline comments
 
@@ -92,27 +92,25 @@ static inline bool IsDefaultGroupID(Grou
 
}
 

	
 
/**
 
 * Checks if a GroupID stands for all vehicles of a company
 
 * @param id_g The GroupID to check
 
 * @return true is id_g is identical to ALL_GROUP
 
 */
 
static inline bool IsAllGroupID(GroupID id_g)
 
{
 
	return id_g == ALL_GROUP;
 
}
 

	
 

	
 
uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e);
 
uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type);
 
uint GetGroupNumProfitVehicle(CompanyID company, GroupID id_g, VehicleType type);
 
Money GetGroupProfitLastYear(CompanyID company, GroupID id_g, VehicleType type);
 

	
 
void SetTrainGroupID(Train *v, GroupID grp);
 
void UpdateTrainGroupID(Train *v);
 
void RemoveVehicleFromGroup(const Vehicle *v);
 
void RemoveAllGroupsForCompany(const CompanyID company);
 
bool GroupIsInGroup(GroupID search, GroupID group);
 

	
 
extern GroupID _new_group_id;
 

	
 
#endif /* GROUP_H */
src/group_cmd.cpp
Show inline comments
 
@@ -4,50 +4,48 @@
 
 * 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 group_cmd.cpp Handling of the engine groups */
 

	
 
#include "stdafx.h"
 
#include "cmd_helper.h"
 
#include "command_func.h"
 
#include "train.h"
 
#include "vehiclelist.h"
 
#include "vehicle_func.h"
 
#include "autoreplace_base.h"
 
#include "autoreplace_func.h"
 
#include "string_func.h"
 
#include "company_func.h"
 
#include "core/pool_func.hpp"
 
#include "order_backup.h"
 
#include "group_cmd.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
GroupID _new_group_id;
 

	
 
GroupPool _group_pool("Group");
 
INSTANTIATE_POOL_METHODS(Group)
 

	
 
GroupStatistics::GroupStatistics()
 
{
 
	this->num_engines = CallocT<uint16>(Engine::GetPoolSize());
 
}
 

	
 
GroupStatistics::~GroupStatistics()
 
{
 
	free(this->num_engines);
 
}
 

	
 
/**
 
 * Clear all caches.
 
 */
 
void GroupStatistics::Clear()
 
{
 
	this->num_vehicle = 0;
 
	this->num_profit_vehicle = 0;
 
	this->profit_last_year = 0;
 

	
 
	/* This is also called when NewGRF change. So the number of engines might have changed. Reallocate. */
 
	free(this->num_engines);
 
@@ -278,84 +276,84 @@ void PropagateChildLivery(const Group *g
 
	for (Group *cg : Group::Iterate()) {
 
		if (cg->parent == g->index) {
 
			if (!HasBit(cg->livery.in_use, 0)) cg->livery.colour1 = g->livery.colour1;
 
			if (!HasBit(cg->livery.in_use, 1)) cg->livery.colour2 = g->livery.colour2;
 
			PropagateChildLivery(cg);
 
		}
 
	}
 
}
 

	
 

	
 
Group::Group(Owner owner)
 
{
 
	this->owner = owner;
 
	this->folded = false;
 
}
 

	
 

	
 
/**
 
 * Create a new vehicle group.
 
 * @param flags type of operation
 
 * @param vt vehicle type
 
 * @param parent_group parent groupid
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdCreateGroup(DoCommandFlag flags, VehicleType vt, GroupID parent_group)
 
std::tuple<CommandCost, GroupID> CmdCreateGroup(DoCommandFlag flags, VehicleType vt, GroupID parent_group)
 
{
 
	if (!IsCompanyBuildableVehicleType(vt)) return CMD_ERROR;
 
	if (!IsCompanyBuildableVehicleType(vt)) return { CMD_ERROR, INVALID_GROUP };
 

	
 
	if (!Group::CanAllocateItem()) return CMD_ERROR;
 
	if (!Group::CanAllocateItem()) return { CMD_ERROR, INVALID_GROUP };
 

	
 
	const Group *pg = Group::GetIfValid(parent_group);
 
	if (pg != nullptr) {
 
		if (pg->owner != _current_company) return CMD_ERROR;
 
		if (pg->vehicle_type != vt) return CMD_ERROR;
 
		if (pg->owner != _current_company) return { CMD_ERROR, INVALID_GROUP };
 
		if (pg->vehicle_type != vt) return { CMD_ERROR, INVALID_GROUP };
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		Group *g = new Group(_current_company);
 
		g->vehicle_type = vt;
 
		g->parent = INVALID_GROUP;
 

	
 
		if (pg == nullptr) {
 
			const Company *c = Company::Get(_current_company);
 
			g->livery.colour1 = c->livery[LS_DEFAULT].colour1;
 
			g->livery.colour2 = c->livery[LS_DEFAULT].colour2;
 
			if (c->settings.renew_keep_length) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
 
		} else {
 
			g->parent = pg->index;
 
			g->livery.colour1 = pg->livery.colour1;
 
			g->livery.colour2 = pg->livery.colour2;
 
			g->flags = pg->flags;
 
		}
 

	
 
		_new_group_id = g->index;
 

	
 
		InvalidateWindowData(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).Pack());
 
		InvalidateWindowData(WC_COMPANY_COLOUR, g->owner, g->vehicle_type);
 

	
 
		return { CommandCost(), g->index };
 
	}
 

	
 
	return CommandCost();
 
	return { CommandCost(), INVALID_GROUP};
 
}
 

	
 

	
 
/**
 
 * Add all vehicles in the given group to the default group and then deletes the group.
 
 * @param flags type of operation
 
 * @param group_id index of group
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdDeleteGroup(DoCommandFlag flags, GroupID group_id)
 
{
 
	Group *g = Group::GetIfValid(group_id);
 
	if (g == nullptr || g->owner != _current_company) return CMD_ERROR;
 

	
 
	/* Remove all vehicles from the group */
 
	Command<CMD_REMOVE_ALL_VEHICLES_GROUP>::Do(flags, group_id);
 

	
 
	/* Delete sub-groups */
 
	for (const Group *gp : Group::Iterate()) {
 
		if (gp->parent == g->index) {
 
			Command<CMD_DELETE_GROUP>::Do(flags, gp->index);
 
		}
 
	}
 

	
 
@@ -476,93 +474,93 @@ static void AddVehicleToGroup(Vehicle *v
 
		case VEH_ROAD:
 
		case VEH_SHIP:
 
		case VEH_AIRCRAFT:
 
			if (v->IsEngineCountable()) UpdateNumEngineGroup(v, v->group_id, new_g);
 
			v->group_id = new_g;
 
			for (Vehicle *u = v; u != nullptr; u = u->Next()) {
 
				u->colourmap = PAL_NONE;
 
				u->InvalidateNewGRFCache();
 
				u->UpdateViewport(true);
 
			}
 
			break;
 
	}
 

	
 
	GroupStatistics::CountVehicle(v, 1);
 
}
 

	
 
/**
 
 * Add a vehicle to a group
 
 * @param flags type of operation
 
 * @param group_id index of group
 
 * @param veh_id vehicle to add to a group
 
 * @param add_shared Add shared vehicles as well.
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared)
 
std::tuple<CommandCost, GroupID> CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared)
 
{
 
	Vehicle *v = Vehicle::GetIfValid(veh_id);
 
	GroupID new_g = group_id;
 

	
 
	if (v == nullptr || (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g) && new_g != NEW_GROUP)) return CMD_ERROR;
 
	if (v == nullptr || (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g) && new_g != NEW_GROUP)) return { CMD_ERROR, INVALID_GROUP };
 

	
 
	if (Group::IsValidID(new_g)) {
 
		Group *g = Group::Get(new_g);
 
		if (g->owner != _current_company || g->vehicle_type != v->type) return CMD_ERROR;
 
		if (g->owner != _current_company || g->vehicle_type != v->type) return { CMD_ERROR, INVALID_GROUP };
 
	}
 

	
 
	if (v->owner != _current_company || !v->IsPrimaryVehicle()) return CMD_ERROR;
 
	if (v->owner != _current_company || !v->IsPrimaryVehicle()) return { CMD_ERROR, INVALID_GROUP };
 

	
 
	if (new_g == NEW_GROUP) {
 
		/* Create new group. */
 
		CommandCost ret = CmdCreateGroup(flags, v->type, INVALID_GROUP);
 
		if (ret.Failed()) return ret;
 
		auto [ret, group_id] = CmdCreateGroup(flags, v->type, INVALID_GROUP);
 
		if (ret.Failed()) return { ret, group_id };
 

	
 
		new_g = _new_group_id;
 
		new_g = group_id;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		AddVehicleToGroup(v, new_g);
 

	
 
		if (add_shared) {
 
			/* Add vehicles in the shared order list as well. */
 
			for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) {
 
				if (v2->group_id != new_g) AddVehicleToGroup(v2, new_g);
 
			}
 
		}
 

	
 
		GroupStatistics::UpdateAutoreplace(v->owner);
 

	
 
		/* Update the Replace Vehicle Windows */
 
		SetWindowDirty(WC_REPLACE_VEHICLE, v->type);
 
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
		SetWindowDirty(WC_VEHICLE_VIEW, v->index);
 
		SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
 
		InvalidateWindowData(GetWindowClassForVehicleType(v->type), VehicleListIdentifier(VL_GROUP_LIST, v->type, _current_company).Pack());
 
		InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindowData(WC_VEHICLE_DETAILS, v->index);
 
	}
 

	
 
	return CommandCost();
 
	return { CommandCost(), new_g };
 
}
 

	
 
/**
 
 * Add all shared vehicles of all vehicles from a group
 
 * @param flags type of operation
 
 * @param id_g index of group
 
 * @param type type of vehicles
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdAddSharedVehicleGroup(DoCommandFlag flags, GroupID id_g, VehicleType type)
 
{
 
	if (!Group::IsValidID(id_g) || !IsCompanyBuildableVehicleType(type)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		/* Find the first front engine which belong to the group id_g
 
		 * then add all shared vehicles of this front engine to the group id_g */
 
		for (const Vehicle *v : Vehicle::Iterate()) {
 
			if (v->type == type && v->IsPrimaryVehicle()) {
 
				if (v->group_id != id_g) continue;
 

	
 
				/* For each shared vehicles add it to the group */
 
				for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) {
 
					if (v2->group_id != id_g) Command<CMD_ADD_VEHICLE_GROUP>::Do(flags, id_g, v2->index, false);
 
				}
src/group_cmd.h
Show inline comments
 
@@ -2,46 +2,46 @@
 
 * 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 group_cmd.h Command definitions related to engine groups. */
 

	
 
#ifndef GROUP_CMD_H
 
#define GROUP_CMD_H
 

	
 
#include "command_type.h"
 
#include "group_type.h"
 
#include "vehicle_type.h"
 

	
 
enum Colours : byte;
 
enum GroupFlags : uint8;
 

	
 
/** Action for \c CmdAlterGroup. */
 
enum class AlterGroupMode : byte {
 
	Rename,    ///< Change group name.
 
	SetParent, ///< Change group parent.
 
};
 

	
 
CommandCost CmdCreateGroup(DoCommandFlag flags, VehicleType vt, GroupID parent_group);
 
std::tuple<CommandCost, GroupID> CmdCreateGroup(DoCommandFlag flags, VehicleType vt, GroupID parent_group);
 
CommandCost CmdAlterGroup(DoCommandFlag flags, AlterGroupMode mode, GroupID group_id, GroupID parent_id, const std::string &text);
 
CommandCost CmdDeleteGroup(DoCommandFlag flags, GroupID group_id);
 
CommandCost CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared);
 
std::tuple<CommandCost, GroupID> CmdAddVehicleGroup(DoCommandFlag flags, GroupID group_id, VehicleID veh_id, bool add_shared);
 
CommandCost CmdAddSharedVehicleGroup(DoCommandFlag flags, GroupID id_g, VehicleType type);
 
CommandCost CmdRemoveAllVehiclesGroup(DoCommandFlag flags, GroupID group_id);
 
CommandCost CmdSetGroupFlag(DoCommandFlag flags, GroupID group_id, GroupFlags flag, bool value, bool recursive);
 
CommandCost CmdSetGroupLivery(DoCommandFlag flags, GroupID group_id, bool primary, Colours colour);
 

	
 
DEF_CMD_TRAIT(CMD_CREATE_GROUP,              CmdCreateGroup,            0, CMDT_ROUTE_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_DELETE_GROUP,              CmdDeleteGroup,            0, CMDT_ROUTE_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_ALTER_GROUP,               CmdAlterGroup,             0, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_ADD_VEHICLE_GROUP,         CmdAddVehicleGroup,        0, CMDT_ROUTE_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_ADD_SHARED_VEHICLE_GROUP,  CmdAddSharedVehicleGroup,  0, CMDT_ROUTE_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_REMOVE_ALL_VEHICLES_GROUP, CmdRemoveAllVehiclesGroup, 0, CMDT_ROUTE_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SET_GROUP_FLAG,            CmdSetGroupFlag,           0, CMDT_ROUTE_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SET_GROUP_LIVERY,          CmdSetGroupLivery,         0, CMDT_ROUTE_MANAGEMENT)
 

	
 
void CcCreateGroup(Commands cmd, const CommandCost &result, VehicleType vt, GroupID parent_group);
 
void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID, VehicleID veh_id, bool);
 
void CcCreateGroup(Commands cmd, const CommandCost &result, GroupID new_group, VehicleType vt, GroupID parent_group);
 
void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID new_group, GroupID, VehicleID veh_id, bool);
 

	
 
#endif /* GROUP_CMD_H */
src/group_gui.cpp
Show inline comments
 
@@ -1122,74 +1122,76 @@ void ShowCompanyGroup(CompanyID company,
 
/**
 
 * Show the group window for the given vehicle.
 
 * @param v The vehicle to show the window for.
 
 */
 
void ShowCompanyGroupForVehicle(const Vehicle *v)
 
{
 
	ShowCompanyGroup(v->owner, v->type, v->group_id, true);
 
}
 

	
 
/**
 
 * Finds a group list window determined by vehicle type and owner
 
 * @param vt vehicle type
 
 * @param owner owner of groups
 
 * @return pointer to VehicleGroupWindow, nullptr if not found
 
 */
 
static inline VehicleGroupWindow *FindVehicleGroupWindow(VehicleType vt, Owner owner)
 
{
 
	return (VehicleGroupWindow *)FindWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, owner).Pack());
 
}
 

	
 
/**
 
 * Opens a 'Rename group' window for newly created group.
 
 * @param veh_type Vehicle type.
 
 */
 
static void CcCreateGroup(VehicleType veh_type)
 
static void CcCreateGroup(GroupID gid, VehicleType veh_type)
 
{
 
	VehicleGroupWindow *w = FindVehicleGroupWindow(veh_type, _current_company);
 
	if (w != nullptr) w->ShowRenameGroupWindow(_new_group_id, true);
 
	if (w != nullptr) w->ShowRenameGroupWindow(gid, true);
 
}
 

	
 
/**
 
 * Opens a 'Rename group' window for newly created group.
 
 * @param cmd Unused.
 
 * @param result Did command succeed?
 
 * @param new_group ID of the created group.
 
 * @param vt Vehicle type.
 
 * @param parent_group Parent group of the enw group.
 
 * @param parent_group Parent group of the new group.
 
 * @see CmdCreateGroup
 
 */
 
void CcCreateGroup(Commands cmd, const CommandCost &result, VehicleType vt, GroupID parent_group)
 
void CcCreateGroup(Commands cmd, const CommandCost &result, GroupID new_group, VehicleType vt, GroupID parent_group)
 
{
 
	if (result.Failed()) return;
 

	
 
	assert(vt <= VEH_AIRCRAFT);
 
	CcCreateGroup(vt);
 
	CcCreateGroup(new_group, vt);
 
}
 

	
 
/**
 
 * Open rename window after adding a vehicle to a new group via drag and drop.
 
 * @param cmd Unused.
 
 * @param result Did command succeed?
 
 * @param new_group ID of the created group.
 
 * @param veh_id vehicle to add to a group
 
 */
 
void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID, VehicleID veh_id, bool)
 
void CcAddVehicleNewGroup(Commands cmd, const CommandCost &result, GroupID new_group, GroupID, VehicleID veh_id, bool)
 
{
 
	if (result.Failed()) return;
 

	
 
	assert(Vehicle::IsValidID(veh_id));
 
	CcCreateGroup(Vehicle::Get(veh_id)->type);
 
	CcCreateGroup(new_group, Vehicle::Get(veh_id)->type);
 
}
 

	
 
/**
 
 * Removes the highlight of a vehicle in a group window
 
 * @param *v Vehicle to remove all highlights from
 
 */
 
void DeleteGroupHighlightOfVehicle(const Vehicle *v)
 
{
 
	/* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any group windows either
 
	 * If that is the case, we can skip looping though the windows and save time
 
	 */
 
	if (_special_mouse_mode != WSM_DRAGDROP) return;
 

	
 
	VehicleGroupWindow *w = FindVehicleGroupWindow(v->type, v->owner);
 
	if (w != nullptr) w->UnselectVehicle(v->index);
 
}
src/script/api/script_object.cpp
Show inline comments
 
@@ -140,130 +140,75 @@ ScriptObject::ActiveInstance::~ActiveIns
 
{
 
	GetStorage()->road_type = road_type;
 
}
 

	
 
/* static */ RoadType ScriptObject::GetRoadType()
 
{
 
	return GetStorage()->road_type;
 
}
 

	
 
/* static */ void ScriptObject::SetRailType(RailType rail_type)
 
{
 
	GetStorage()->rail_type = rail_type;
 
}
 

	
 
/* static */ RailType ScriptObject::GetRailType()
 
{
 
	return GetStorage()->rail_type;
 
}
 

	
 
/* static */ void ScriptObject::SetLastCommandRes(bool res)
 
{
 
	GetStorage()->last_command_res = res;
 
	/* Also store the results of various global variables */
 
	SetNewVehicleID(_new_vehicle_id);
 
	SetNewSignID(_new_sign_id);
 
	SetNewGroupID(_new_group_id);
 
	SetNewGoalID(_new_goal_id);
 
	SetNewStoryPageID(_new_story_page_id);
 
	SetNewStoryPageElementID(_new_story_page_element_id);
 
}
 

	
 
/* static */ bool ScriptObject::GetLastCommandRes()
 
{
 
	return GetStorage()->last_command_res;
 
}
 

	
 
/* static */ void ScriptObject::SetLastCommandResData(CommandDataBuffer data)
 
{
 
	GetStorage()->last_cmd_ret = std::move(data);
 
}
 

	
 
/* static */ const CommandDataBuffer &ScriptObject::GetLastCommandResData()
 
{
 
	return GetStorage()->last_cmd_ret;
 
}
 

	
 
/* static */ void ScriptObject::SetNewVehicleID(VehicleID vehicle_id)
 
{
 
	GetStorage()->new_vehicle_id = vehicle_id;
 
}
 

	
 
/* static */ VehicleID ScriptObject::GetNewVehicleID()
 
{
 
	return GetStorage()->new_vehicle_id;
 
}
 

	
 
/* static */ void ScriptObject::SetNewSignID(SignID sign_id)
 
{
 
	GetStorage()->new_sign_id = sign_id;
 
}
 

	
 
/* static */ SignID ScriptObject::GetNewSignID()
 
{
 
	return GetStorage()->new_sign_id;
 
}
 

	
 
/* static */ void ScriptObject::SetNewGroupID(GroupID group_id)
 
{
 
	GetStorage()->new_group_id = group_id;
 
}
 

	
 
/* static */ GroupID ScriptObject::GetNewGroupID()
 
{
 
	return GetStorage()->new_group_id;
 
}
 

	
 
/* static */ void ScriptObject::SetNewGoalID(GoalID goal_id)
 
{
 
	GetStorage()->new_goal_id = goal_id;
 
}
 

	
 
/* static */ GroupID ScriptObject::GetNewGoalID()
 
{
 
	return GetStorage()->new_goal_id;
 
}
 

	
 
/* static */ void ScriptObject::SetNewStoryPageID(StoryPageID story_page_id)
 
{
 
	GetStorage()->new_story_page_id = story_page_id;
 
}
 

	
 
/* static */ GroupID ScriptObject::GetNewStoryPageID()
 
{
 
	return GetStorage()->new_story_page_id;
 
}
 

	
 
/* static */ void ScriptObject::SetNewStoryPageElementID(StoryPageElementID story_page_element_id)
 
{
 
	GetStorage()->new_story_page_element_id = story_page_element_id;
 
}
 

	
 
/* static */ GroupID ScriptObject::GetNewStoryPageElementID()
 
{
 
	return GetStorage()->new_story_page_element_id;
 
}
 

	
 
/* static */ void ScriptObject::SetAllowDoCommand(bool allow)
 
{
 
	GetStorage()->allow_do_command = allow;
 
}
 

	
 
/* static */ bool ScriptObject::GetAllowDoCommand()
 
{
 
	return GetStorage()->allow_do_command;
 
}
 

	
 
/* static */ void ScriptObject::SetCompany(CompanyID company)
 
{
 
	if (GetStorage()->root_company == INVALID_OWNER) GetStorage()->root_company = company;
 
	GetStorage()->company = company;
 

	
 
	_current_company = company;
 
}
 

	
 
/* static */ CompanyID ScriptObject::GetCompany()
 
{
 
	return GetStorage()->company;
 
}
 

	
 
/* static */ CompanyID ScriptObject::GetRootCompany()
src/script/api/script_object.hpp
Show inline comments
 
@@ -177,73 +177,48 @@ protected:
 
	 */
 
	static void SetDoCommandDelay(uint ticks);
 

	
 
	/**
 
	 * Get the delay of the DoCommand.
 
	 */
 
	static uint GetDoCommandDelay();
 

	
 
	/**
 
	 * Get the latest result of a DoCommand.
 
	 */
 
	static bool GetLastCommandRes();
 

	
 
	/**
 
	 * Get the extra return data from the last DoCommand.
 
	 */
 
	static const CommandDataBuffer &GetLastCommandResData();
 

	
 
	/**
 
	 * Get the latest stored new_vehicle_id.
 
	 */
 
	static VehicleID GetNewVehicleID();
 

	
 
	/**
 
	 * Get the latest stored new_sign_id.
 
	 */
 
	static SignID GetNewSignID();
 

	
 
	/**
 
	 * Get the latest stored new_group_id.
 
	 */
 
	static GroupID GetNewGroupID();
 

	
 
	/**
 
	 * Get the latest stored new_goal_id.
 
	 */
 
	static GoalID GetNewGoalID();
 

	
 
	/**
 
	 * Get the latest stored new_story_page_id.
 
	 */
 
	static StoryPageID GetNewStoryPageID();
 

	
 
	/**
 
	 * Get the latest stored new_story_page_id.
 
	 */
 
	static StoryPageID GetNewStoryPageElementID();
 

	
 
	/**
 
	 * Store a allow_do_command per company.
 
	 * @param allow The new allow.
 
	 */
 
	static void SetAllowDoCommand(bool allow);
 

	
 
	/**
 
	 * Get the internal value of allow_do_command. This can differ
 
	 * from CanSuspend() if the reason we are not allowed
 
	 * to execute a DoCommand is in squirrel and not the API.
 
	 * In that case use this function to restore the previous value.
 
	 * @return True iff DoCommands are allowed in the current scope.
 
	 */
 
	static bool GetAllowDoCommand();
 

	
 
	/**
 
	 * Set the current company to execute commands for or request
 
	 *  information about.
 
	 * @param company The new company.
 
	 */
 
	static void SetCompany(CompanyID company);
 

	
 
	/**
 
	 * Get the current company we are executing commands for or
 
	 *  requesting information about.
 
@@ -284,78 +259,48 @@ protected:
 
	static bool CanSuspend();
 

	
 
	/**
 
	 * Get the pointer to store event data in.
 
	 */
 
	static void *&GetEventPointer();
 

	
 
	/**
 
	 * Get the pointer to store log message in.
 
	 */
 
	static void *&GetLogPointer();
 

	
 
	/**
 
	 * Get an allocated string with all control codes stripped off.
 
	 */
 
	static char *GetString(StringID string);
 

	
 
private:
 
	/**
 
	 * Store a new_vehicle_id per company.
 
	 * @param vehicle_id The new VehicleID.
 
	 */
 
	static void SetNewVehicleID(VehicleID vehicle_id);
 

	
 
	/**
 
	 * Store a new_sign_id per company.
 
	 * @param sign_id The new SignID.
 
	 */
 
	static void SetNewSignID(SignID sign_id);
 

	
 
	/**
 
	 * Store a new_group_id per company.
 
	 * @param group_id The new GroupID.
 
	 */
 
	static void SetNewGroupID(GroupID group_id);
 

	
 
	/**
 
	 * Store a new_goal_id per company.
 
	 * @param goal_id The new GoalID.
 
	 */
 
	static void SetNewGoalID(GoalID goal_id);
 

	
 
	/**
 
	 * Store a new_story_page_id per company.
 
	 * @param story_page_id The new StoryPageID.
 
	 */
 
	static void SetNewStoryPageID(StoryPageID story_page_id);
 

	
 
	/**
 
	 * Store a new_story_page_id per company.
 
	 * @param story_page_id The new StoryPageID.
 
	 */
 
	static void SetNewStoryPageElementID(StoryPageElementID story_page_element_id);
 

	
 
	/* Helper functions for DoCommand. */
 
	static std::tuple<bool, bool, bool> DoCommandPrep();
 
	static bool DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only);
 
	static CommandCallbackData *GetDoCommandCallback();
 
};
 

	
 
namespace ScriptObjectInternal {
 
	/** Validate a single string argument coming from network. */
 
	template <class T>
 
	static inline void SanitizeSingleStringHelper(T &data)
 
	{
 
		if constexpr (std::is_same_v<std::string, T>) {
 
			/* The string must be valid, i.e. not contain special codes. Since some
 
			 * can be made with GSText, make sure the control codes are removed. */
 
			data = ::StrMakeValid(data, SVS_NONE);
 
		}
 
	}
 

	
 
	/** Helper function to perform validation on command data strings. */
 
	template<class Ttuple, size_t... Tindices>
 
	static inline void SanitizeStringsHelper(Ttuple &values, std::index_sequence<Tindices...>)
 
	{
 
		((SanitizeSingleStringHelper(std::get<Tindices>(values))), ...);
 
	}
src/script/script_instance.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 script_instance.cpp Implementation of ScriptInstance. */
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "../saveload/saveload.h"
 

	
 
#include "../script/squirrel_class.hpp"
 

	
 
#include "script_fatalerror.hpp"
 
#include "script_storage.hpp"
 
#include "script_info.hpp"
 
#include "script_instance.hpp"
 

	
 
#include "api/script_controller.hpp"
 
#include "api/script_error.hpp"
 
#include "api/script_event.hpp"
 
#include "api/script_log.hpp"
 

	
 
#include "../company_base.h"
 
#include "../company_func.h"
 
#include "../fileio_func.h"
 
#include "../misc/endian_buffer.hpp"
 

	
 
#include "../safeguards.h"
 

	
 
ScriptStorage::~ScriptStorage()
 
{
 
	/* Free our pointers */
 
	if (event_data != nullptr) ScriptEventController::FreeEventPointer();
 
	if (log_data != nullptr) ScriptLog::FreeLogPointer();
 
}
 

	
 
/**
 
 * Callback called by squirrel when a script uses "print" and for error messages.
 
 * @param error_msg Is this an error message?
 
 * @param message The actual message text.
 
 */
 
static void PrintFunc(bool error_msg, const SQChar *message)
 
{
 
	/* Convert to OpenTTD internal capable string */
 
	ScriptController::Print(error_msg, message);
 
}
 

	
 
ScriptInstance::ScriptInstance(const char *APIName) :
 
	engine(nullptr),
 
	versionAPI(nullptr),
 
@@ -253,69 +254,69 @@ void ScriptInstance::GameLoop()
 
		this->Died();
 
	}
 
}
 

	
 
void ScriptInstance::CollectGarbage()
 
{
 
	if (this->is_started && !this->IsDead()) {
 
		ScriptObject::ActiveInstance active(this);
 
		this->engine->CollectGarbage();
 
	}
 
}
 

	
 
/* static */ void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
 
{
 
	instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
 
}
 

	
 
/* static */ void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
 
{
 
	instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
 
}
 

	
 
/* static */ void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
 
{
 
	instance->engine->InsertResult(ScriptObject::GetNewSignID());
 
	instance->engine->InsertResult(EndianBufferReader::ToValue<SignID>(ScriptObject::GetLastCommandResData()));
 
}
 

	
 
/* static */ void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
 
{
 
	instance->engine->InsertResult(ScriptObject::GetNewGroupID());
 
	instance->engine->InsertResult(EndianBufferReader::ToValue<GroupID>(ScriptObject::GetLastCommandResData()));
 
}
 

	
 
/* static */ void ScriptInstance::DoCommandReturnGoalID(ScriptInstance *instance)
 
{
 
	instance->engine->InsertResult(ScriptObject::GetNewGoalID());
 
	instance->engine->InsertResult(EndianBufferReader::ToValue<GoalID>(ScriptObject::GetLastCommandResData()));
 
}
 

	
 
/* static */ void ScriptInstance::DoCommandReturnStoryPageID(ScriptInstance *instance)
 
{
 
	instance->engine->InsertResult(ScriptObject::GetNewStoryPageID());
 
	instance->engine->InsertResult(EndianBufferReader::ToValue<StoryPageID>(ScriptObject::GetLastCommandResData()));
 
}
 

	
 
/* static */ void ScriptInstance::DoCommandReturnStoryPageElementID(ScriptInstance *instance)
 
{
 
	instance->engine->InsertResult(ScriptObject::GetNewStoryPageElementID());
 
	instance->engine->InsertResult(EndianBufferReader::ToValue<StoryPageElementID>(ScriptObject::GetLastCommandResData()));
 
}
 

	
 
ScriptStorage *ScriptInstance::GetStorage()
 
{
 
	return this->storage;
 
}
 

	
 
void *ScriptInstance::GetLogPointer()
 
{
 
	ScriptObject::ActiveInstance active(this);
 

	
 
	return ScriptObject::GetLogPointer();
 
}
 

	
 
/*
 
 * All data is stored in the following format:
 
 * First 1 byte indicating if there is a data blob at all.
 
 * 1 byte indicating the type of data.
 
 * The data itself, this differs per type:
 
 *  - integer: a binary representation of the integer (int32).
 
 *  - string:  First one byte with the string length, then a 0-terminated char
 
 *             array. The string can't be longer than 255 bytes (including
 
 *             terminating '\0').
 
 *  - array:   All data-elements of the array are saved recursive in this
src/script/script_storage.hpp
Show inline comments
 
@@ -29,69 +29,59 @@ typedef bool (ScriptModeProc)();
 
 * The storage for each script. It keeps track of important information.
 
 */
 
class ScriptStorage {
 
friend class ScriptObject;
 
private:
 
	ScriptModeProc *mode;             ///< The current build mode we are int.
 
	class ScriptObject *mode_instance; ///< The instance belonging to the current build mode.
 
	CompanyID root_company;          ///< The root company, the company that the script really belongs to.
 
	CompanyID company;               ///< The current company.
 

	
 
	uint delay;                      ///< The ticks of delay each DoCommand has.
 
	bool allow_do_command;           ///< Is the usage of DoCommands restricted?
 

	
 
	CommandCost costs;               ///< The costs the script is tracking.
 
	Money last_cost;                 ///< The last cost of the command.
 
	uint last_error;                 ///< The last error of the command.
 
	bool last_command_res;           ///< The last result of the command.
 

	
 
	TileIndex last_tile;             ///< The last tile passed to a command.
 
	CommandDataBuffer last_data;     ///< The last data passed to a command.
 
	Commands last_cmd;               ///< The last cmd passed to a command.
 
	CommandDataBuffer last_cmd_ret;  ///< The extra data returned by the last command.
 

	
 
	VehicleID new_vehicle_id;        ///< The ID of the new Vehicle.
 
	SignID new_sign_id;              ///< The ID of the new Sign.
 
	GroupID new_group_id;            ///< The ID of the new Group.
 
	GoalID new_goal_id;              ///< The ID of the new Goal.
 
	StoryPageID new_story_page_id;   ///< The ID of the new StoryPage.
 
	StoryPageID new_story_page_element_id; ///< The ID of the new StoryPageElement.
 

	
 
	std::vector<int> callback_value; ///< The values which need to survive a callback.
 

	
 
	RoadType road_type;              ///< The current roadtype we build.
 
	RailType rail_type;              ///< The current railtype we build.
 

	
 
	void *event_data;                ///< Pointer to the event data storage.
 
	void *log_data;                  ///< Pointer to the log data storage.
 

	
 
public:
 
	ScriptStorage() :
 
		mode              (nullptr),
 
		mode_instance     (nullptr),
 
		root_company      (INVALID_OWNER),
 
		company           (INVALID_OWNER),
 
		delay             (1),
 
		allow_do_command  (true),
 
		/* costs (can't be set) */
 
		last_cost         (0),
 
		last_error        (STR_NULL),
 
		last_command_res  (true),
 
		last_tile         (INVALID_TILE),
 
		last_cmd          (CMD_END),
 
		new_vehicle_id    (0),
 
		new_sign_id       (0),
 
		new_group_id      (0),
 
		new_goal_id       (0),
 
		new_story_page_id (0),
 
		new_story_page_element_id(0),
 
		/* calback_value (can't be set) */
 
		road_type         (INVALID_ROADTYPE),
 
		rail_type         (INVALID_RAILTYPE),
 
		event_data        (nullptr),
 
		log_data          (nullptr)
 
	{ }
 

	
 
	~ScriptStorage();
 
};
 

	
 
#endif /* SCRIPT_STORAGE_HPP */
src/signs_cmd.cpp
Show inline comments
 
@@ -2,128 +2,125 @@
 
 * 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 signs_cmd.cpp Handling of sign related commands. */
 

	
 
#include "stdafx.h"
 
#include "landscape.h"
 
#include "company_func.h"
 
#include "signs_base.h"
 
#include "signs_func.h"
 
#include "command_func.h"
 
#include "tilehighlight_func.h"
 
#include "viewport_kdtree.h"
 
#include "window_func.h"
 
#include "string_func.h"
 
#include "signs_cmd.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/** The last built sign. */
 
SignID _new_sign_id;
 

	
 
/**
 
 * Place a sign at the given coordinates. Ownership of sign has
 
 * no effect whatsoever except for the colour the sign gets for easy recognition,
 
 * but everybody is able to rename/remove it.
 
 * @param tile tile to place sign at
 
 * @param flags type of operation
 
 * @param text contents of the sign
 
 * @return the cost of this operation or an error
 
 * @return the cost of this operation + the ID of the new sign or an error
 
 */
 
CommandCost CmdPlaceSign(DoCommandFlag flags, TileIndex tile, const std::string &text)
 
std::tuple<CommandCost, SignID> CmdPlaceSign(DoCommandFlag flags, TileIndex tile, const std::string &text)
 
{
 
	/* Try to locate a new sign */
 
	if (!Sign::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_SIGNS);
 
	if (!Sign::CanAllocateItem()) return { CommandCost(STR_ERROR_TOO_MANY_SIGNS), INVALID_SIGN };
 

	
 
	/* Check sign text length if any */
 
	if (Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return CMD_ERROR;
 
	if (Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return { CMD_ERROR, INVALID_SIGN };
 

	
 
	/* When we execute, really make the sign */
 
	if (flags & DC_EXEC) {
 
		Sign *si = new Sign(_game_mode == GM_EDITOR ? OWNER_DEITY : _current_company);
 
		int x = TileX(tile) * TILE_SIZE;
 
		int y = TileY(tile) * TILE_SIZE;
 

	
 
		si->x = x;
 
		si->y = y;
 
		si->z = GetSlopePixelZ(x, y);
 
		if (!text.empty()) {
 
			si->name = text;
 
		}
 
		si->UpdateVirtCoord();
 
		InvalidateWindowData(WC_SIGN_LIST, 0, 0);
 
		_new_sign_id = si->index;
 
		return { CommandCost(), si->index };
 
	}
 

	
 
	return CommandCost();
 
	return { CommandCost(), INVALID_SIGN };
 
}
 

	
 
/**
 
 * Rename a sign. If the new name of the sign is empty, we assume
 
 * the user wanted to delete it. So delete it. Ownership of signs
 
 * has no meaning/effect whatsoever except for eyecandy
 
 * @param flags type of operation
 
 * @param sign_id index of the sign to be renamed/removed
 
 * @param text the new name or an empty string when resetting to the default
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdRenameSign(DoCommandFlag flags, SignID sign_id, const std::string &text)
 
{
 
	Sign *si = Sign::GetIfValid(sign_id);
 
	if (si == nullptr) return CMD_ERROR;
 
	if (!CompanyCanRenameSign(si)) return CMD_ERROR;
 

	
 
	/* Rename the signs when empty, otherwise remove it */
 
	if (!text.empty()) {
 
		if (Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			/* Assign the new one */
 
			si->name = text;
 
			if (_game_mode != GM_EDITOR) si->owner = _current_company;
 

	
 
			si->UpdateVirtCoord();
 
			InvalidateWindowData(WC_SIGN_LIST, 0, 1);
 
		}
 
	} else { // Delete sign
 
		if (flags & DC_EXEC) {
 
			si->sign.MarkDirty();
 
			if (si->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeSign(si->index));
 
			delete si;
 

	
 
			InvalidateWindowData(WC_SIGN_LIST, 0, 0);
 
		}
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Callback function that is called after a sign is placed
 
 * @param cmd unused
 
 * @param result of the operation
 
 * @param tile unused
 
 * @param new_sign ID of the placed sign.
 
 */
 
void CcPlaceSign(Commands cmd, const CommandCost &result, TileIndex tile)
 
void CcPlaceSign(Commands cmd, const CommandCost &result, SignID new_sign)
 
{
 
	if (result.Failed()) return;
 

	
 
	ShowRenameSignWindow(Sign::Get(_new_sign_id));
 
	ShowRenameSignWindow(Sign::Get(new_sign));
 
	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)
 
{
 
	Command<CMD_PLACE_SIGN>::Post(STR_ERROR_CAN_T_PLACE_SIGN_HERE, CcPlaceSign, tile, {});
 
}
src/signs_cmd.h
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 signs_cmd.h Command definitions related to signs. */
 

	
 
#ifndef SIGNS_CMD_H
 
#define SIGNS_CMD_H
 

	
 
#include "command_type.h"
 
#include "signs_type.h"
 

	
 
CommandCost CmdPlaceSign(DoCommandFlag flags, TileIndex tile, const std::string &text);
 
std::tuple<CommandCost, SignID> CmdPlaceSign(DoCommandFlag flags, TileIndex tile, const std::string &text);
 
CommandCost CmdRenameSign(DoCommandFlag flags, SignID sign_id, const std::string &text);
 

	
 
DEF_CMD_TRAIT(CMD_PLACE_SIGN,  CmdPlaceSign,  CMD_DEITY, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_RENAME_SIGN, CmdRenameSign, CMD_DEITY, CMDT_OTHER_MANAGEMENT)
 

	
 
void CcPlaceSign(Commands cmd, const CommandCost &result, SignID new_sign);
 

	
 
#endif /* SIGNS_CMD_H */
src/signs_func.h
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 signs_func.h Functions related to signs. */
 

	
 
#ifndef SIGNS_FUNC_H
 
#define SIGNS_FUNC_H
 

	
 
#include "signs_type.h"
 
#include "tile_type.h"
 

	
 
struct Window;
 
extern SignID _new_sign_id;
 

	
 
void UpdateAllSignVirtCoords();
 
void PlaceProc_Sign(TileIndex tile);
 
bool CompanyCanRenameSign(const Sign *si);
 

	
 
/* signs_gui.cpp */
 
void ShowRenameSignWindow(const Sign *si);
 
void HandleClickOnSign(const Sign *si);
 
void DeleteRenameSignWindow(SignID sign);
 

	
 
Window *ShowSignList();
 

	
 
#endif /* SIGNS_FUNC_H */
src/story.cpp
Show inline comments
 
@@ -9,50 +9,48 @@
 

	
 
#include "stdafx.h"
 
#include "story_base.h"
 
#include "core/pool_func.hpp"
 
#include "cmd_helper.h"
 
#include "command_func.h"
 
#include "company_base.h"
 
#include "company_func.h"
 
#include "string_func.h"
 
#include "date_func.h"
 
#include "tile_map.h"
 
#include "goal_type.h"
 
#include "goal_base.h"
 
#include "window_func.h"
 
#include "gui.h"
 
#include "vehicle_base.h"
 
#include "game/game.hpp"
 
#include "script/api/script_story_page.hpp"
 
#include "script/api/script_event_types.hpp"
 
#include "story_cmd.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
StoryPageElementID _new_story_page_element_id;
 
StoryPageID _new_story_page_id;
 
uint32 _story_page_element_next_sort_value;
 
uint32 _story_page_next_sort_value;
 

	
 
StoryPageElementPool _story_page_element_pool("StoryPageElement");
 
StoryPagePool _story_page_pool("StoryPage");
 
INSTANTIATE_POOL_METHODS(StoryPageElement)
 
INSTANTIATE_POOL_METHODS(StoryPage)
 

	
 
/**
 
 * This helper for Create/Update PageElement Cmd procedure verifies if the page
 
 * element parameters are correct for the given page element type.
 
 * @param page_id The page id of the page which the page element (will) belong to
 
 * @param type The type of the page element to create/update
 
 * @param tile The tile parameter of the DoCommand proc
 
 * @param reference The reference parameter of the DoCommand proc (p2)
 
 * @param text The text parameter of the DoCommand proc
 
 * @return true, if and only if the given parameters are valid for the given page element type and page id.
 
 */
 
static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElementType type, TileIndex tile, uint32 reference, const char *text)
 
{
 
	StoryPageButtonData button_data{ reference };
 

	
 
	switch (type) {
 
		case SPET_TEXT:
 
@@ -181,125 +179,126 @@ bool StoryPageButtonData::ValidateFlags(
 
	if (flags & ~(SPBF_FLOAT_LEFT | SPBF_FLOAT_RIGHT)) return false;
 
	return true;
 
}
 

	
 
/** Verify that the data stores a valid StoryPageButtonCursor value */
 
bool StoryPageButtonData::ValidateCursor() const
 
{
 
	return GB(this->referenced_id, 8, 8) < SPBC_END;
 
}
 

	
 
/** Verity that the data stored a valid VehicleType value */
 
bool StoryPageButtonData::ValidateVehicleType() const
 
{
 
	byte vehtype = GB(this->referenced_id, 16, 8);
 
	return vehtype == VEH_INVALID || vehtype < VEH_COMPANY_END;
 
}
 

	
 
/**
 
 * Create a new story page.
 
 * @param flags type of operation
 
 * @param company Company for which this story page belongs to.
 
 * @param text Title of the story page. Null is allowed in which case a generic page title is provided by OpenTTD.
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text)
 
std::tuple<CommandCost, StoryPageID> CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text)
 
{
 
	if (!StoryPage::CanAllocateItem()) return CMD_ERROR;
 
	if (!StoryPage::CanAllocateItem()) return { CMD_ERROR, INVALID_STORY_PAGE };
 

	
 
	if (_current_company != OWNER_DEITY) return CMD_ERROR;
 
	if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
 
	if (_current_company != OWNER_DEITY) return { CMD_ERROR, INVALID_STORY_PAGE };
 
	if (company != INVALID_COMPANY && !Company::IsValidID(company)) return { CMD_ERROR, INVALID_STORY_PAGE };
 

	
 
	if (flags & DC_EXEC) {
 
		if (_story_page_pool.items == 0) {
 
			/* Initialize the next sort value variable. */
 
			_story_page_next_sort_value = 0;
 
		}
 

	
 
		StoryPage *s = new StoryPage();
 
		s->sort_value = _story_page_next_sort_value;
 
		s->date = _date;
 
		s->company = company;
 
		if (text.empty()) {
 
			s->title = nullptr;
 
		} else {
 
			s->title = stredup(text.c_str());
 
		}
 

	
 
		InvalidateWindowClassesData(WC_STORY_BOOK, -1);
 
		if (StoryPage::GetNumItems() == 1) InvalidateWindowData(WC_MAIN_TOOLBAR, 0);
 

	
 
		_new_story_page_id = s->index;
 
		_story_page_next_sort_value++;
 
		return { CommandCost(), s->index };
 
	}
 

	
 
	return CommandCost();
 
	return { CommandCost(), INVALID_STORY_PAGE };
 
}
 

	
 
/**
 
 * Create a new story page element.
 
 * @param flags type of operation
 
 * @param tile Tile location if it is a location page element, otherwise unused.
 
 * @param page_id The page which the element belongs to.
 
 * @param type Page element type
 
 * @param reference Id of referenced object
 
 * @param text Text content in case it is a text or location page element
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageID page_id, StoryPageElementType type, uint32 reference, const std::string &text)
 
std::tuple<CommandCost, StoryPageElementID> CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageID page_id, StoryPageElementType type, uint32 reference, const std::string &text)
 
{
 
	if (!StoryPageElement::CanAllocateItem()) return CMD_ERROR;
 
	if (!StoryPageElement::CanAllocateItem()) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
 

	
 
	/* Allow at most 128 elements per page. */
 
	uint16 element_count = 0;
 
	for (StoryPageElement *iter : StoryPageElement::Iterate()) {
 
		if (iter->page == page_id) element_count++;
 
	}
 
	if (element_count >= 128) return CMD_ERROR;
 
	if (element_count >= 128) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
 

	
 
	if (_current_company != OWNER_DEITY) return CMD_ERROR;
 
	if (!StoryPage::IsValidID(page_id)) return CMD_ERROR;
 
	if (!VerifyElementContentParameters(page_id, type, tile, reference, text.c_str())) return CMD_ERROR;
 
	if (_current_company != OWNER_DEITY) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
 
	if (!StoryPage::IsValidID(page_id)) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
 
	if (!VerifyElementContentParameters(page_id, type, tile, reference, text.c_str())) return { CMD_ERROR, INVALID_STORY_PAGE_ELEMENT };
 

	
 

	
 
	if (flags & DC_EXEC) {
 
		if (_story_page_element_pool.items == 0) {
 
			/* Initialize the next sort value variable. */
 
			_story_page_element_next_sort_value = 0;
 
		}
 

	
 
		StoryPageElement *pe = new StoryPageElement();
 
		pe->sort_value = _story_page_element_next_sort_value;
 
		pe->type = type;
 
		pe->page = page_id;
 
		UpdateElement(*pe, tile, reference, text.c_str());
 

	
 
		InvalidateWindowClassesData(WC_STORY_BOOK, page_id);
 

	
 
		_new_story_page_element_id = pe->index;
 
		_story_page_element_next_sort_value++;
 
		return { CommandCost(), pe->index };
 
	}
 

	
 
	return CommandCost();
 
	return { CommandCost(), INVALID_STORY_PAGE_ELEMENT };
 
}
 

	
 
/**
 
 * Update a new story page element.
 
 * @param flags type of operation
 
 * @param tile Tile location if it is a location page element, otherwise unused.
 
 * @param page_element_id The page element to update.
 
 * @param reference Id of referenced object
 
 * @param text Text content in case it is a text or location page element
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdUpdateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageElementID page_element_id, uint32 reference, const std::string &text)
 
{
 
	if (_current_company != OWNER_DEITY) return CMD_ERROR;
 
	if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR;
 

	
 
	StoryPageElement *pe = StoryPageElement::Get(page_element_id);
 
	StoryPageID page_id = pe->page;
 
	StoryPageElementType type = pe->type;
 

	
 
	if (!VerifyElementContentParameters(page_id, type, tile, reference, text.c_str())) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		UpdateElement(*pe, tile, reference, text.c_str());
src/story_cmd.h
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 story_cmd.h Command definitions related to stories. */
 

	
 
#ifndef STORY_CMD_H
 
#define STORY_CMD_H
 

	
 
#include "command_type.h"
 
#include "company_type.h"
 
#include "date_type.h"
 
#include "story_type.h"
 
#include "vehicle_type.h"
 

	
 
CommandCost CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text);
 
CommandCost CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageID page_id, StoryPageElementType type, uint32 reference, const std::string &text);
 
std::tuple<CommandCost, StoryPageID> CmdCreateStoryPage(DoCommandFlag flags, CompanyID company, const std::string &text);
 
std::tuple<CommandCost, StoryPageElementID> CmdCreateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageID page_id, StoryPageElementType type, uint32 reference, const std::string &text);
 
CommandCost CmdUpdateStoryPageElement(DoCommandFlag flags, TileIndex tile, StoryPageElementID page_element_id, uint32 reference, const std::string &text);
 
CommandCost CmdSetStoryPageTitle(DoCommandFlag flags, StoryPageID page_id, const std::string &text);
 
CommandCost CmdSetStoryPageDate(DoCommandFlag flags, StoryPageID page_id, Date date);
 
CommandCost CmdShowStoryPage(DoCommandFlag flags, StoryPageID page_id);
 
CommandCost CmdRemoveStoryPage(DoCommandFlag flags, StoryPageID page_id);
 
CommandCost CmdRemoveStoryPageElement(DoCommandFlag flags, StoryPageElementID page_element_id);
 
CommandCost CmdStoryPageButton(DoCommandFlag flags, TileIndex tile, StoryPageElementID page_element_id, VehicleID reference);
 

	
 
DEF_CMD_TRAIT(CMD_CREATE_STORY_PAGE,         CmdCreateStoryPage,        CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_CREATE_STORY_PAGE_ELEMENT, CmdCreateStoryPageElement, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_UPDATE_STORY_PAGE_ELEMENT, CmdUpdateStoryPageElement, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SET_STORY_PAGE_TITLE,      CmdSetStoryPageTitle,      CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SET_STORY_PAGE_DATE,       CmdSetStoryPageDate,       CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_SHOW_STORY_PAGE,           CmdShowStoryPage,          CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_REMOVE_STORY_PAGE,         CmdRemoveStoryPage,        CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_REMOVE_STORY_PAGE_ELEMENT, CmdRemoveStoryPageElement, CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_STORY_PAGE_BUTTON,         CmdStoryPageButton,        CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 

	
 
#endif /* STORY_CMD_H */
src/story_type.h
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 story_type.h basic types related to story pages */
 

	
 
#ifndef STORY_TYPE_H
 
#define STORY_TYPE_H
 

	
 
#include "core/enum_type.hpp"
 

	
 
typedef uint16 StoryPageElementID; ///< ID of a story page element
 
typedef uint16 StoryPageID; ///< ID of a story page
 
struct StoryPageElement;
 
struct StoryPage;
 
enum StoryPageElementType : byte;
 

	
 
extern StoryPageElementID _new_story_page_element_id;
 
extern StoryPageID _new_story_page_id;
 
static const StoryPageElementID INVALID_STORY_PAGE_ELEMENT = 0xFFFF; ///< Constant representing a non-existing story page element.
 
static const StoryPageID INVALID_STORY_PAGE = 0xFFFF; ///< Constant representing a non-existing story page.
 

	
 
#endif /* STORY_TYPE_H */
 

	
src/terraform_cmd.h
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 terraform_cmd.h Command definitions related to terraforming. */
 

	
 
#ifndef TERRAFORM_CMD_H
 
#define TERRAFORM_CMD_H
 

	
 
#include "command_type.h"
 
#include "map_type.h"
 
#include "slope_type.h"
 

	
 
CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, bool dir_up);
 
CommandCost CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal, LevelMode lm);
 

	
 
DEF_CMD_TRAIT(CMD_TERRAFORM_LAND, CmdTerraformLand, CMD_ALL_TILES | CMD_AUTO,               CMDT_LANDSCAPE_CONSTRUCTION)
 
DEF_CMD_TRAIT(CMD_LEVEL_LAND,     CmdLevelLand,     CMD_ALL_TILES | CMD_AUTO | CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION) // test run might clear tiles multiple times, in execution that only happens once
 

	
 
CommandCallback CcPlaySound_EXPLOSION;
 
CommandCallback CcPlaceSign;
 
CommandCallback CcTerraform;
 

	
 
#endif /* TERRAFORM_CMD_H */
src/town.h
Show inline comments
 
@@ -207,49 +207,48 @@ const CargoSpec *FindFirstCargoWithTownE
 

	
 
/** Town actions of a company. */
 
enum TownActions {
 
	TACT_NONE             = 0x00, ///< Empty action set.
 

	
 
	TACT_ADVERTISE_SMALL  = 0x01, ///< Small advertising campaign.
 
	TACT_ADVERTISE_MEDIUM = 0x02, ///< Medium advertising campaign.
 
	TACT_ADVERTISE_LARGE  = 0x04, ///< Large advertising campaign.
 
	TACT_ROAD_REBUILD     = 0x08, ///< Rebuild the roads.
 
	TACT_BUILD_STATUE     = 0x10, ///< Build a statue.
 
	TACT_FUND_BUILDINGS   = 0x20, ///< Fund new buildings.
 
	TACT_BUY_RIGHTS       = 0x40, ///< Buy exclusive transport rights.
 
	TACT_BRIBE            = 0x80, ///< Try to bribe the council.
 

	
 
	TACT_COUNT            = 8,    ///< Number of available town actions.
 

	
 
	TACT_ADVERTISE        = TACT_ADVERTISE_SMALL | TACT_ADVERTISE_MEDIUM | TACT_ADVERTISE_LARGE, ///< All possible advertising actions.
 
	TACT_CONSTRUCTION     = TACT_ROAD_REBUILD | TACT_BUILD_STATUE | TACT_FUND_BUILDINGS,         ///< All possible construction actions.
 
	TACT_FUNDS            = TACT_BUY_RIGHTS | TACT_BRIBE,                                        ///< All possible funding actions.
 
	TACT_ALL              = TACT_ADVERTISE | TACT_CONSTRUCTION | TACT_FUNDS,                     ///< All possible actions.
 
};
 
DECLARE_ENUM_AS_BIT_SET(TownActions)
 

	
 
extern const byte _town_action_costs[TACT_COUNT];
 
extern TownID _new_town_id;
 

	
 
/**
 
 * Set the default name for a depot/waypoint
 
 * @tparam T The type/class to make a default name for
 
 * @param obj The object/instance we want to find the name for
 
 */
 
template <class T>
 
void MakeDefaultName(T *obj)
 
{
 
	/* We only want to set names if it hasn't been set before, or when we're calling from afterload. */
 
	assert(obj->name.empty() || obj->town_cn == UINT16_MAX);
 

	
 
	obj->town = ClosestTownFromTile(obj->xy, UINT_MAX);
 

	
 
	/* Find first unused number belonging to this town. This can never fail,
 
	 * as long as there can be at most 65535 waypoints/depots in total.
 
	 *
 
	 * This does 'n * m' search, but with 32bit 'used' bitmap, it needs at
 
	 * most 'n * (1 + ceil(m / 32))' steps (n - number of waypoints in pool,
 
	 * m - number of waypoints near this town).
 
	 * Usually, it needs only 'n' steps.
 
	 *
 
	 * If it wasn't using 'used' and 'idx', it would just search for increasing 'next',
 
	 * but this way it is faster */
src/town_cmd.cpp
Show inline comments
 
@@ -38,50 +38,48 @@
 
#include "date_func.h"
 
#include "subsidy_func.h"
 
#include "core/pool_func.hpp"
 
#include "town.h"
 
#include "town_kdtree.h"
 
#include "townname_func.h"
 
#include "core/random_func.hpp"
 
#include "core/backup_type.hpp"
 
#include "depot_base.h"
 
#include "object_map.h"
 
#include "object_base.h"
 
#include "ai/ai.hpp"
 
#include "game/game.hpp"
 
#include "town_cmd.h"
 
#include "landscape_cmd.h"
 
#include "road_cmd.h"
 
#include "terraform_cmd.h"
 
#include "tunnelbridge_cmd.h"
 

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

	
 
#include "safeguards.h"
 

	
 
TownID _new_town_id;
 

	
 
/* Initialize the town-pool */
 
TownPool _town_pool("Town");
 
INSTANTIATE_POOL_METHODS(Town)
 

	
 

	
 
TownKdtree _town_kdtree(&Kdtree_TownXYFunc);
 

	
 
void RebuildTownKdtree()
 
{
 
	std::vector<TownID> townids;
 
	for (const Town *town : Town::Iterate()) {
 
		townids.push_back(town->index);
 
	}
 
	_town_kdtree.Build(townids.begin(), townids.end());
 
}
 

	
 

	
 
/**
 
 * Check if a town 'owns' a bridge.
 
 * Bridges to not directly have an owner, so we check the tiles adjacent to the bridge ends.
 
 * If either adjacent tile belongs to the town then it will be assumed that the town built
 
 * the bridge.
 
 * @param tile Bridge tile to test
 
 * @param t Town we are interested in
 
@@ -1916,144 +1914,145 @@ static CommandCost TownCanBePlacedHere(T
 
 * @param name name to check
 
 * @return is this name unique?
 
 */
 
static bool IsUniqueTownName(const std::string &name)
 
{
 
	for (const Town *t : Town::Iterate()) {
 
		if (!t->name.empty() && t->name == name) return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Create a new town.
 
 * @param flags type of operation
 
 * @param tile coordinates where town is built
 
 * @param size size of the town (@see TownSize)
 
 * @param city true iff it should be a city
 
 * @param layout town road layout (@see TownLayout)
 
 * @param random_location use random location (randomize \c tile )
 
 * @param townnameparts town name parts
 
 * @param text Custom name for the town. If empty, the town name parts will be used.
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdFoundTown(DoCommandFlag flags, TileIndex tile, TownSize size, bool city, TownLayout layout, bool random_location, uint32 townnameparts, const std::string &text)
 
std::tuple<CommandCost, TownID> CmdFoundTown(DoCommandFlag flags, TileIndex tile, TownSize size, bool city, TownLayout layout, bool random_location, uint32 townnameparts, const std::string &text)
 
{
 
	TownNameParams par(_settings_game.game_creation.town_name);
 

	
 
	if (size >= TSZ_END) return CMD_ERROR;
 
	if (layout >= NUM_TLS) return CMD_ERROR;
 
	if (size >= TSZ_END) return { CMD_ERROR, INVALID_TOWN };
 
	if (layout >= NUM_TLS) return { CMD_ERROR, INVALID_TOWN };
 

	
 
	/* Some things are allowed only in the scenario editor and for game scripts. */
 
	if (_game_mode != GM_EDITOR && _current_company != OWNER_DEITY) {
 
		if (_settings_game.economy.found_town == TF_FORBIDDEN) return CMD_ERROR;
 
		if (size == TSZ_LARGE) return CMD_ERROR;
 
		if (random_location) return CMD_ERROR;
 
		if (_settings_game.economy.found_town == TF_FORBIDDEN) return { CMD_ERROR, INVALID_TOWN };
 
		if (size == TSZ_LARGE) return { CMD_ERROR, INVALID_TOWN };
 
		if (random_location) return { CMD_ERROR, INVALID_TOWN };
 
		if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT && layout != _settings_game.economy.town_layout) {
 
			return CMD_ERROR;
 
			return { CMD_ERROR, INVALID_TOWN };
 
		}
 
	} else if (_current_company == OWNER_DEITY && random_location) {
 
		/* Random parameter is not allowed for Game Scripts. */
 
		return CMD_ERROR;
 
		return { CMD_ERROR, INVALID_TOWN };
 
	}
 

	
 
	if (text.empty()) {
 
		/* If supplied name is empty, townnameparts has to generate unique automatic name */
 
		if (!VerifyTownName(townnameparts, &par)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
 
		if (!VerifyTownName(townnameparts, &par)) return { CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE), INVALID_TOWN };
 
	} else {
 
		/* If name is not empty, it has to be unique custom name */
 
		if (Utf8StringLength(text) >= MAX_LENGTH_TOWN_NAME_CHARS) return CMD_ERROR;
 
		if (!IsUniqueTownName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
 
		if (Utf8StringLength(text) >= MAX_LENGTH_TOWN_NAME_CHARS) return { CMD_ERROR, INVALID_TOWN };
 
		if (!IsUniqueTownName(text)) return { CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE), INVALID_TOWN };
 
	}
 

	
 
	/* Allocate town struct */
 
	if (!Town::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_TOWNS);
 
	if (!Town::CanAllocateItem()) return { CommandCost(STR_ERROR_TOO_MANY_TOWNS), INVALID_TOWN };
 

	
 
	if (!random_location) {
 
		CommandCost ret = TownCanBePlacedHere(tile);
 
		if (ret.Failed()) return ret;
 
		if (ret.Failed()) return { ret, INVALID_TOWN };
 
	}
 

	
 
	static const byte price_mult[][TSZ_RANDOM + 1] = {{ 15, 25, 40, 25 }, { 20, 35, 55, 35 }};
 
	/* multidimensional arrays have to have defined length of non-first dimension */
 
	static_assert(lengthof(price_mult[0]) == 4);
 

	
 
	CommandCost cost(EXPENSES_OTHER, _price[PR_BUILD_TOWN]);
 
	byte mult = price_mult[city][size];
 

	
 
	cost.MultiplyCost(mult);
 

	
 
	/* Create the town */
 
	TownID new_town = INVALID_TOWN;
 
	if (flags & DC_EXEC) {
 
		if (cost.GetCost() > GetAvailableMoneyForCommand()) {
 
			_additional_cash_required = cost.GetCost();
 
			return CommandCost(EXPENSES_OTHER);
 
			return { CommandCost(EXPENSES_OTHER), INVALID_TOWN };
 
		}
 

	
 
		Backup<bool> old_generating_world(_generating_world, true, FILE_LINE);
 
		UpdateNearestTownForRoadTiles(true);
 
		Town *t;
 
		if (random_location) {
 
			t = CreateRandomTown(20, townnameparts, size, city, layout);
 
			if (t == nullptr) {
 
				cost = CommandCost(STR_ERROR_NO_SPACE_FOR_TOWN);
 
			} else {
 
				_new_town_id = t->index;
 
				new_town = t->index;
 
			}
 
		} else {
 
			t = new Town(tile);
 
			DoCreateTown(t, tile, townnameparts, size, city, layout, true);
 
		}
 
		UpdateNearestTownForRoadTiles(false);
 
		old_generating_world.Restore();
 

	
 
		if (t != nullptr && !text.empty()) {
 
			t->name = text;
 
			t->UpdateVirtCoord();
 
		}
 

	
 
		if (_game_mode != GM_EDITOR) {
 
			/* 't' can't be nullptr since 'random' is false outside scenedit */
 
			assert(!random_location);
 

	
 
			if (_current_company == OWNER_DEITY) {
 
				SetDParam(0, t->index);
 
				AddTileNewsItem(STR_NEWS_NEW_TOWN_UNSPONSORED, NT_INDUSTRY_OPEN, tile);
 
			} else {
 
				SetDParam(0, _current_company);
 
				NewsStringData *company_name = new NewsStringData(GetString(STR_COMPANY_NAME));
 

	
 
				SetDParamStr(0, company_name->string);
 
				SetDParam(1, t->index);
 

	
 
				AddTileNewsItem(STR_NEWS_NEW_TOWN, NT_INDUSTRY_OPEN, tile, company_name);
 
			}
 
			AI::BroadcastNewEvent(new ScriptEventTownFounded(t->index));
 
			Game::NewEvent(new ScriptEventTownFounded(t->index));
 
		}
 
	}
 
	return cost;
 
	return { cost, new_town };
 
}
 

	
 
/**
 
 * Towns must all be placed on the same grid or when they eventually
 
 * interpenetrate their road networks will not mesh nicely; this
 
 * function adjusts a tile so that it aligns properly.
 
 *
 
 * @param tile the tile to start at
 
 * @param layout which town layout algo is in effect
 
 * @return the adjusted tile
 
 */
 
static TileIndex AlignTileToGrid(TileIndex tile, TownLayout layout)
 
{
 
	switch (layout) {
 
		case TL_2X2_GRID: return TileXY(TileX(tile) - TileX(tile) % 3, TileY(tile) - TileY(tile) % 3);
 
		case TL_3X3_GRID: return TileXY(TileX(tile) & ~3, TileY(tile) & ~3);
 
		default:          return tile;
 
	}
 
}
 

	
 
/**
 
 * Towns must all be placed on the same grid or when they eventually
 
 * interpenetrate their road networks will not mesh nicely; this
 
 * function tells you if a tile is properly aligned.
src/town_cmd.h
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 town_cmd.h Command definitions related to towns. */
 

	
 
#ifndef TOWN_CMD_H
 
#define TOWN_CMD_H
 

	
 
#include "command_type.h"
 
#include "company_type.h"
 
#include "town_type.h"
 

	
 
enum TownEffect : byte;
 

	
 
CommandCost CmdFoundTown(DoCommandFlag flags, TileIndex tile, TownSize size, bool city, TownLayout layout, bool random_location, uint32 townnameparts, const std::string &text);
 
std::tuple<CommandCost, TownID> CmdFoundTown(DoCommandFlag flags, TileIndex tile, TownSize size, bool city, TownLayout layout, bool random_location, uint32 townnameparts, const std::string &text);
 
CommandCost CmdRenameTown(DoCommandFlag flags, TownID town_id, const std::string &text);
 
CommandCost CmdDoTownAction(DoCommandFlag flags, TownID town_id, uint8 action);
 
CommandCost CmdTownGrowthRate(DoCommandFlag flags, TownID town_id, uint16 growth_rate);
 
CommandCost CmdTownRating(DoCommandFlag flags, TownID town_id, CompanyID company_id, int16 rating);
 
CommandCost CmdTownCargoGoal(DoCommandFlag flags, TownID town_id, TownEffect te, uint32 goal);
 
CommandCost CmdTownSetText(DoCommandFlag flags, TownID town_id, const std::string &text);
 
CommandCost CmdExpandTown(DoCommandFlag flags, TownID town_id, uint32 grow_amount);
 
CommandCost CmdDeleteTown(DoCommandFlag flags, TownID town_id);
 

	
 
DEF_CMD_TRAIT(CMD_FOUND_TOWN,       CmdFoundTown,      CMD_DEITY | CMD_NO_TEST,  CMDT_LANDSCAPE_CONSTRUCTION) // founding random town can fail only in exec run
 
DEF_CMD_TRAIT(CMD_RENAME_TOWN,      CmdRenameTown,     CMD_DEITY | CMD_SERVER,   CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_DO_TOWN_ACTION,   CmdDoTownAction,   0,                        CMDT_LANDSCAPE_CONSTRUCTION)
 
DEF_CMD_TRAIT(CMD_TOWN_CARGO_GOAL,  CmdTownCargoGoal,  CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_TOWN_GROWTH_RATE, CmdTownGrowthRate, CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_TOWN_RATING,      CmdTownRating,     CMD_DEITY,                CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_TOWN_SET_TEXT,    CmdTownSetText,    CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
 
DEF_CMD_TRAIT(CMD_EXPAND_TOWN,      CmdExpandTown,     CMD_DEITY,                CMDT_LANDSCAPE_CONSTRUCTION)
 
DEF_CMD_TRAIT(CMD_DELETE_TOWN,      CmdDeleteTown,     CMD_OFFLINE,              CMDT_LANDSCAPE_CONSTRUCTION)
 

	
 
CommandCallback CcFoundTown;
 
CommandCallback CcFoundRandomTown;
 
void CcFoundRandomTown(Commands cmd, const CommandCost &result, TownID town_id);
 

	
 
#endif /* TOWN_CMD_H */
src/town_gui.cpp
Show inline comments
 
@@ -996,51 +996,51 @@ GUITownList::SortFunction * const TownDi
 
	&TownRatingSorter,
 
};
 

	
 
static WindowDesc _town_directory_desc(
 
	WDP_AUTO, "list_towns", 208, 202,
 
	WC_TOWN_DIRECTORY, WC_NONE,
 
	0,
 
	_nested_town_directory_widgets, lengthof(_nested_town_directory_widgets)
 
);
 

	
 
void ShowTownDirectory()
 
{
 
	if (BringWindowToFrontById(WC_TOWN_DIRECTORY, 0)) return;
 
	new TownDirectoryWindow(&_town_directory_desc);
 
}
 

	
 
void CcFoundTown(Commands cmd, const CommandCost &result, TileIndex tile)
 
{
 
	if (result.Failed()) return;
 

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

	
 
void CcFoundRandomTown(Commands cmd, const CommandCost &result, TileIndex tile)
 
void CcFoundRandomTown(Commands cmd, const CommandCost &result, TownID town_id)
 
{
 
	if (result.Succeeded()) ScrollMainWindowToTile(Town::Get(_new_town_id)->xy);
 
	if (result.Succeeded()) ScrollMainWindowToTile(Town::Get(town_id)->xy);
 
}
 

	
 
static const NWidgetPart _nested_found_town_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
 
		NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FOUND_TOWN_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
 
		NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
 
	EndContainer(),
 
	/* Construct new town(s) buttons. */
 
	NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
 
		NWidget(NWID_SPACER), SetMinimalSize(0, 2),
 
		NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_NEW_TOWN), SetMinimalSize(156, 12), SetFill(1, 0),
 
										SetDataTip(STR_FOUND_TOWN_NEW_TOWN_BUTTON, STR_FOUND_TOWN_NEW_TOWN_TOOLTIP), SetPadding(0, 2, 1, 2),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_RANDOM_TOWN), SetMinimalSize(156, 12), SetFill(1, 0),
 
										SetDataTip(STR_FOUND_TOWN_RANDOM_TOWN_BUTTON, STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP), SetPadding(0, 2, 1, 2),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_MANY_RANDOM_TOWNS), SetMinimalSize(156, 12), SetFill(1, 0),
 
										SetDataTip(STR_FOUND_TOWN_MANY_RANDOM_TOWNS, STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP), SetPadding(0, 2, 0, 2),
 
		/* Town name selection. */
 
		NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(156, 14), SetPadding(0, 2, 0, 2), SetDataTip(STR_FOUND_TOWN_NAME_TITLE, STR_NULL),
 
		NWidget(WWT_EDITBOX, COLOUR_GREY, WID_TF_TOWN_NAME_EDITBOX), SetMinimalSize(156, 12), SetPadding(0, 2, 3, 2),
 
										SetDataTip(STR_FOUND_TOWN_NAME_EDITOR_TITLE, STR_FOUND_TOWN_NAME_EDITOR_HELP),
 
		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_TOWN_NAME_RANDOM), SetMinimalSize(78, 12), SetPadding(0, 2, 0, 2), SetFill(1, 0),
 
										SetDataTip(STR_FOUND_TOWN_NAME_RANDOM_BUTTON, STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP),
 
@@ -1129,49 +1129,50 @@ public:
 
	}
 

	
 
	void UpdateButtons(bool check_availability)
 
	{
 
		if (check_availability && _game_mode != GM_EDITOR) {
 
			this->SetWidgetsDisabledState(true, WID_TF_RANDOM_TOWN, WID_TF_MANY_RANDOM_TOWNS, WID_TF_SIZE_LARGE, WIDGET_LIST_END);
 
			this->SetWidgetsDisabledState(_settings_game.economy.found_town != TF_CUSTOM_LAYOUT,
 
					WID_TF_LAYOUT_ORIGINAL, WID_TF_LAYOUT_BETTER, WID_TF_LAYOUT_GRID2, WID_TF_LAYOUT_GRID3, WID_TF_LAYOUT_RANDOM, WIDGET_LIST_END);
 
			if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT) town_layout = _settings_game.economy.town_layout;
 
		}
 

	
 
		for (int i = WID_TF_SIZE_SMALL; i <= WID_TF_SIZE_RANDOM; i++) {
 
			this->SetWidgetLoweredState(i, i == WID_TF_SIZE_SMALL + this->town_size);
 
		}
 

	
 
		this->SetWidgetLoweredState(WID_TF_CITY, this->city);
 

	
 
		for (int i = WID_TF_LAYOUT_ORIGINAL; i <= WID_TF_LAYOUT_RANDOM; i++) {
 
			this->SetWidgetLoweredState(i, i == WID_TF_LAYOUT_ORIGINAL + this->town_layout);
 
		}
 

	
 
		this->SetDirty();
 
	}
 

	
 
	void ExecuteFoundTownCommand(TileIndex tile, bool random, StringID errstr, CommandCallback cc)
 
	template <typename Tcallback>
 
	void ExecuteFoundTownCommand(TileIndex tile, bool random, StringID errstr, Tcallback cc)
 
	{
 
		std::string name;
 

	
 
		if (!this->townnamevalid) {
 
			name = this->townname_editbox.text.buf;
 
		} else {
 
			/* If user changed the name, send it */
 
			char buf[MAX_LENGTH_TOWN_NAME_CHARS * MAX_CHAR_LENGTH];
 
			GetTownName(buf, &this->params, this->townnameparts, lastof(buf));
 
			if (strcmp(buf, this->townname_editbox.text.buf) != 0) name = this->townname_editbox.text.buf;
 
		}
 

	
 
		bool success = Command<CMD_FOUND_TOWN>::Post(errstr, cc,
 
				tile, this->town_size, this->city, this->town_layout, random, townnameparts, name);
 

	
 
		/* Rerandomise name, if success and no cost-estimation. */
 
		if (success && !_shift_pressed) this->RandomTownName();
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
	{
 
		switch (widget) {
 
			case WID_TF_NEW_TOWN:
 
				HandlePlacePushButton(this, WID_TF_NEW_TOWN, SPR_CURSOR_TOWN, HT_RECT);
0 comments (0 inline, 0 general)