Codechange: Don't use a global for the 'not enough cash' message.

This commit is contained in:
Michael Lutz 2021-12-01 00:17:05 +01:00
parent 41fa16f325
commit 2e39637db2
12 changed files with 63 additions and 55 deletions

View File

@ -287,9 +287,6 @@ void CommandHelperBase::LogCommandExecution(Commands cmd, StringID err_message,
*/
bool CommandHelperBase::InternalExecutePrepTest(CommandFlags cmd_flags, TileIndex tile, Backup<CompanyID> &cur_company)
{
/* Reset the state. */
_additional_cash_required = 0;
/* Do not even think about executing out-of-bounds tile-commands */
if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (cmd_flags & CMD_ALL_TILES) == 0))) return false;
@ -356,11 +353,12 @@ std::tuple<bool, bool, bool> CommandHelperBase::InternalExecuteValidateTestAndPr
* @param cmd_flags Command flags.
* @param res_test Command result of test run.
* @param tes_exec Command result of real run.
* @param extra_cash Additional cash required for successful command execution.
* @param tile Tile of command execution.
* @param[in,out] cur_company Backup of current company at start of command execution.
* @return Final command result.
*/
CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, TileIndex tile, Backup<CompanyID> &cur_company)
CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, Money extra_cash, TileIndex tile, Backup<CompanyID> &cur_company)
{
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_COMMAND);
@ -389,11 +387,11 @@ CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, Comman
/* If we're needing more money and we haven't done
* anything yet, ask for the money! */
if (_additional_cash_required != 0 && res_exec.GetCost() == 0) {
if (extra_cash != 0 && res_exec.GetCost() == 0) {
/* It could happen we removed rail, thus gained money, and deleted something else.
* So make sure the signal buffer is empty even in this case */
UpdateSignalsInBuffer();
SetDParam(0, _additional_cash_required);
SetDParam(0, extra_cash);
return CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY);
}

View File

@ -39,8 +39,6 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID);
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);
@ -103,7 +101,7 @@ protected:
static void InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd);
static bool InternalExecutePrepTest(CommandFlags cmd_flags, TileIndex tile, Backup<CompanyID> &cur_company);
static std::tuple<bool, bool, bool> InternalExecuteValidateTestAndPrepExec(CommandCost &res, CommandFlags cmd_flags, bool estimate_only, bool network_command, Backup<CompanyID> &cur_company);
static CommandCost InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, TileIndex tile, Backup<CompanyID> &cur_company);
static CommandCost InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, Money extra_cash, TileIndex tile, Backup<CompanyID> &cur_company);
static void LogCommandExecution(Commands cmd, StringID err_message, TileIndex tile, const CommandDataBuffer &args, bool failed);
};
@ -354,6 +352,16 @@ protected:
return (ClientIdIsSet(std::get<Tindices>(values)) && ...);
}
template<class Ttuple>
static inline Money ExtractAdditionalMoney(Ttuple &values)
{
if constexpr (std::is_same_v<std::tuple_element_t<1, Tret>, Money>) {
return std::get<1>(values);
} else {
return {};
}
}
static Tret Execute(StringID err_message, CommandCallback *callback, bool my_cmd, bool estimate_only, bool network_command, TileIndex tile, std::tuple<Targs...> args)
{
/* Prevent recursion; it gives a mess over the network */
@ -403,10 +411,17 @@ protected:
/* Actually try and execute the command. */
Tret res2 = std::apply(CommandTraits<Tcmd>::proc, std::tuple_cat(std::make_tuple(flags | DC_EXEC), args));
/* Convention: If the second result element is of type Money,
* this is the additional cash required for the command. */
Money additional_money{};
if constexpr (!std::is_same_v<Tret, CommandCost>) { // No short-circuiting for 'if constexpr'.
additional_money = ExtractAdditionalMoney(res2);
}
if constexpr (std::is_same_v<Tret, CommandCost>) {
return InternalExecuteProcessResult(Tcmd, cmd_flags, res, res2, tile, cur_company);
return InternalExecuteProcessResult(Tcmd, cmd_flags, res, res2, additional_money, tile, cur_company);
} else {
std::get<0>(res2) = InternalExecuteProcessResult(Tcmd, cmd_flags, ExtractCommandCost(res), ExtractCommandCost(res2), tile, cur_company);
std::get<0>(res2) = InternalExecuteProcessResult(Tcmd, cmd_flags, ExtractCommandCost(res), ExtractCommandCost(res2), additional_money, tile, cur_company);
return res2;
}
}

View File

@ -99,7 +99,6 @@ const ScoreInfo _score_info[] = {
int64 _score_part[MAX_COMPANIES][SCORE_END];
Economy _economy;
Prices _price;
Money _additional_cash_required;
static PriceMultipliers _price_base_multiplier;
/**

View File

@ -736,9 +736,9 @@ CommandCost CmdLandscapeClear(DoCommandFlag flags, TileIndex tile)
* @param diagonal Whether to use the Orthogonal (false) or Diagonal (true) iterator.
* @return the cost of this operation or an error
*/
CommandCost CmdClearArea(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal)
std::tuple<CommandCost, Money> CmdClearArea(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal)
{
if (start_tile >= MapSize()) return CMD_ERROR;
if (start_tile >= MapSize()) return { CMD_ERROR, 0 };
Money money = GetAvailableMoneyForCommand();
CommandCost cost(EXPENSES_CONSTRUCTION);
@ -764,9 +764,8 @@ CommandCost CmdClearArea(DoCommandFlag flags, TileIndex tile, TileIndex start_ti
if (flags & DC_EXEC) {
money -= ret.GetCost();
if (ret.GetCost() > 0 && money < 0) {
_additional_cash_required = ret.GetCost();
delete iter;
return cost;
return { cost, ret.GetCost() };
}
Command<CMD_LANDSCAPE_CLEAR>::Do(flags, t);
@ -786,7 +785,7 @@ CommandCost CmdClearArea(DoCommandFlag flags, TileIndex tile, TileIndex start_ti
}
delete iter;
return had_success ? cost : last_error;
return { had_success ? cost : last_error, 0 };
}

View File

@ -13,7 +13,7 @@
#include "command_type.h"
CommandCost CmdLandscapeClear(DoCommandFlag flags, TileIndex tile);
CommandCost CmdClearArea(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal);
std::tuple<CommandCost, Money> CmdClearArea(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal);
DEF_CMD_TRAIT(CMD_LANDSCAPE_CLEAR, CmdLandscapeClear, CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_CLEAR_AREA, CmdClearArea, CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION) // destroying multi-tile houses makes town rating differ between test and execution

View File

@ -1070,16 +1070,16 @@ CommandCost CmdBuildLongRoad(DoCommandFlag flags, TileIndex start_tile, TileInde
* @param end_half end tile starts in the 2nd half of tile (p2 & 2)
* @return the cost of this operation or an error
*/
CommandCost CmdRemoveLongRoad(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile, RoadType rt, Axis axis, bool start_half, bool end_half)
std::tuple<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile, RoadType rt, Axis axis, bool start_half, bool end_half)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
if (end_tile >= MapSize()) return CMD_ERROR;
if (!ValParamRoadType(rt) || !IsEnumValid(axis)) return CMD_ERROR;
if (end_tile >= MapSize()) return { CMD_ERROR, 0 };
if (!ValParamRoadType(rt) || !IsEnumValid(axis)) return { CMD_ERROR, 0 };
/* Only drag in X or Y direction dictated by the direction variable */
if (axis == AXIS_X && TileY(start_tile) != TileY(end_tile)) return CMD_ERROR; // x-axis
if (axis == AXIS_Y && TileX(start_tile) != TileX(end_tile)) return CMD_ERROR; // y-axis
if (axis == AXIS_X && TileY(start_tile) != TileY(end_tile)) return { CMD_ERROR, 0 }; // x-axis
if (axis == AXIS_Y && TileX(start_tile) != TileX(end_tile)) return { CMD_ERROR, 0 }; // y-axis
/* Swap start and ending tile, also the half-tile drag vars. */
if (start_tile > end_tile || (start_tile == end_tile && start_half)) {
@ -1107,8 +1107,7 @@ CommandCost CmdRemoveLongRoad(DoCommandFlag flags, TileIndex start_tile, TileInd
if (flags & DC_EXEC) {
money_spent += ret.GetCost();
if (money_spent > 0 && money_spent > money_available) {
_additional_cash_required = Command<CMD_REMOVE_LONG_ROAD>::Do(flags & ~DC_EXEC, start_tile, end_tile, rt, axis, start_half, end_half).GetCost();
return cost;
return { cost, std::get<0>(Command<CMD_REMOVE_LONG_ROAD>::Do(flags & ~DC_EXEC, start_tile, end_tile, rt, axis, start_half, end_half)).GetCost() };
}
RemoveRoad(tile, flags, bits, rtt, true, false);
}
@ -1125,7 +1124,7 @@ CommandCost CmdRemoveLongRoad(DoCommandFlag flags, TileIndex start_tile, TileInd
tile += (axis == AXIS_Y) ? TileDiffXY(0, 1) : TileDiffXY(1, 0);
}
return had_success ? cost : last_error;
return { had_success ? cost : last_error, 0 };
}
/**

View File

@ -18,7 +18,7 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt);
void UpdateNearestTownForRoadTiles(bool invalidate);
CommandCost CmdBuildLongRoad(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile, RoadType rt, Axis axis, DisallowedRoadDirections drd, bool start_half, bool end_half, bool is_ai);
CommandCost CmdRemoveLongRoad(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile, RoadType rt, Axis axis, bool start_half, bool end_half);
std::tuple<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile, RoadType rt, Axis axis, bool start_half, bool end_half);
CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, RoadType rt, DisallowedRoadDirections toggle_drd, TownID town_id);
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir);
CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RoadType to_type);

View File

@ -341,9 +341,9 @@ CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, b
* @param LevelMode Mode of leveling \c LevelMode.
* @return the cost of this operation or an error
*/
CommandCost CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal, LevelMode lm)
std::tuple<CommandCost, Money> CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal, LevelMode lm)
{
if (start_tile >= MapSize()) return CMD_ERROR;
if (start_tile >= MapSize()) return { CMD_ERROR, 0 };
_terraform_err_tile = INVALID_TILE;
@ -356,11 +356,11 @@ CommandCost CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_ti
case LM_LEVEL: break;
case LM_RAISE: h++; break;
case LM_LOWER: h--; break;
default: return CMD_ERROR;
default: return { CMD_ERROR, 0 };
}
/* Check range of destination height */
if (h > _settings_game.construction.map_height_limit) return_cmd_error((oldh == 0) ? STR_ERROR_ALREADY_AT_SEA_LEVEL : STR_ERROR_TOO_HIGH);
if (h > _settings_game.construction.map_height_limit) return { CommandCost(oldh == 0 ? STR_ERROR_ALREADY_AT_SEA_LEVEL : STR_ERROR_TOO_HIGH), 0 };
Money money = GetAvailableMoneyForCommand();
CommandCost cost(EXPENSES_CONSTRUCTION);
@ -369,7 +369,7 @@ CommandCost CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_ti
const Company *c = Company::GetIfValid(_current_company);
int limit = (c == nullptr ? INT32_MAX : GB(c->terraform_limit, 16, 16));
if (limit == 0) return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED);
if (limit == 0) return { CommandCost(STR_ERROR_TERRAFORM_LIMIT_REACHED), 0 };
TileIterator *iter = diagonal ? (TileIterator *)new DiagonalTileIterator(tile, start_tile) : new OrthogonalTileIterator(tile, start_tile);
for (; *iter != INVALID_TILE; ++(*iter)) {
@ -388,9 +388,8 @@ CommandCost CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_ti
if (flags & DC_EXEC) {
money -= ret.GetCost();
if (money < 0) {
_additional_cash_required = ret.GetCost();
delete iter;
return cost;
return { cost, ret.GetCost() };
}
Command<CMD_TERRAFORM_LAND>::Do(flags, t, SLOPE_N, curh <= h);
} else {
@ -414,5 +413,5 @@ CommandCost CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_ti
}
delete iter;
return had_success ? cost : last_error;
return { had_success ? cost : last_error, 0 };
}

View File

@ -15,7 +15,7 @@
#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);
std::tuple<CommandCost, Money> 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

View File

@ -1935,41 +1935,41 @@ static bool IsUniqueTownName(const std::string &name)
* @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
*/
std::tuple<CommandCost, TownID> CmdFoundTown(DoCommandFlag flags, TileIndex tile, TownSize size, bool city, TownLayout layout, bool random_location, uint32 townnameparts, const std::string &text)
std::tuple<CommandCost, Money, 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, INVALID_TOWN };
if (layout >= NUM_TLS) return { CMD_ERROR, INVALID_TOWN };
if (size >= TSZ_END) return { CMD_ERROR, 0, INVALID_TOWN };
if (layout >= NUM_TLS) return { CMD_ERROR, 0, 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, 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_FORBIDDEN) return { CMD_ERROR, 0, INVALID_TOWN };
if (size == TSZ_LARGE) return { CMD_ERROR, 0, INVALID_TOWN };
if (random_location) return { CMD_ERROR, 0, INVALID_TOWN };
if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT && layout != _settings_game.economy.town_layout) {
return { CMD_ERROR, INVALID_TOWN };
return { CMD_ERROR, 0, INVALID_TOWN };
}
} else if (_current_company == OWNER_DEITY && random_location) {
/* Random parameter is not allowed for Game Scripts. */
return { CMD_ERROR, INVALID_TOWN };
return { CMD_ERROR, 0, INVALID_TOWN };
}
if (text.empty()) {
/* If supplied name is empty, townnameparts has to generate unique automatic name */
if (!VerifyTownName(townnameparts, &par)) return { CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE), INVALID_TOWN };
if (!VerifyTownName(townnameparts, &par)) return { CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE), 0, 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, INVALID_TOWN };
if (!IsUniqueTownName(text)) return { CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE), INVALID_TOWN };
if (Utf8StringLength(text) >= MAX_LENGTH_TOWN_NAME_CHARS) return { CMD_ERROR, 0, INVALID_TOWN };
if (!IsUniqueTownName(text)) return { CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE), 0, INVALID_TOWN };
}
/* Allocate town struct */
if (!Town::CanAllocateItem()) return { CommandCost(STR_ERROR_TOO_MANY_TOWNS), INVALID_TOWN };
if (!Town::CanAllocateItem()) return { CommandCost(STR_ERROR_TOO_MANY_TOWNS), 0, INVALID_TOWN };
if (!random_location) {
CommandCost ret = TownCanBePlacedHere(tile);
if (ret.Failed()) return { ret, INVALID_TOWN };
if (ret.Failed()) return { ret, 0, INVALID_TOWN };
}
static const byte price_mult[][TSZ_RANDOM + 1] = {{ 15, 25, 40, 25 }, { 20, 35, 55, 35 }};
@ -1985,8 +1985,7 @@ std::tuple<CommandCost, TownID> CmdFoundTown(DoCommandFlag flags, TileIndex tile
TownID new_town = INVALID_TOWN;
if (flags & DC_EXEC) {
if (cost.GetCost() > GetAvailableMoneyForCommand()) {
_additional_cash_required = cost.GetCost();
return { CommandCost(EXPENSES_OTHER), INVALID_TOWN };
return { CommandCost(EXPENSES_OTHER), cost.GetCost(), INVALID_TOWN };
}
Backup<bool> old_generating_world(_generating_world, true, FILE_LINE);
@ -2031,7 +2030,7 @@ std::tuple<CommandCost, TownID> CmdFoundTown(DoCommandFlag flags, TileIndex tile
Game::NewEvent(new ScriptEventTownFounded(t->index));
}
}
return { cost, new_town };
return { cost, 0, new_town };
}
/**

View File

@ -16,7 +16,7 @@
enum TownEffect : byte;
std::tuple<CommandCost, TownID> CmdFoundTown(DoCommandFlag flags, TileIndex tile, TownSize size, bool city, TownLayout layout, bool random_location, uint32 townnameparts, const std::string &text);
std::tuple<CommandCost, Money, 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);
@ -37,6 +37,6 @@ DEF_CMD_TRAIT(CMD_EXPAND_TOWN, CmdExpandTown, CMD_DEITY,
DEF_CMD_TRAIT(CMD_DELETE_TOWN, CmdDeleteTown, CMD_OFFLINE, CMDT_LANDSCAPE_CONSTRUCTION)
CommandCallback CcFoundTown;
void CcFoundRandomTown(Commands cmd, const CommandCost &result, TownID town_id);
void CcFoundRandomTown(Commands cmd, const CommandCost &result, Money, TownID town_id);
#endif /* TOWN_CMD_H */

View File

@ -1017,7 +1017,7 @@ void CcFoundTown(Commands cmd, const CommandCost &result, TileIndex tile)
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
}
void CcFoundRandomTown(Commands cmd, const CommandCost &result, TownID town_id)
void CcFoundRandomTown(Commands cmd, const CommandCost &result, Money, TownID town_id)
{
if (result.Succeeded()) ScrollMainWindowToTile(Town::Get(town_id)->xy);
}