diff options
author | Michael Lutz <michi@icosahedron.de> | 2021-10-30 01:31:46 +0200 |
---|---|---|
committer | Michael Lutz <michi@icosahedron.de> | 2021-12-16 22:28:32 +0100 |
commit | e740c24eb7a90bc771f5976d64d80639ee7576e5 (patch) | |
tree | b3c8f0c87419cb11c106ba1cb58d6ae0648beef4 /src/command_func.h | |
parent | c88b104ec662ea80bec89f58aa7ad9d0baac7704 (diff) | |
download | openttd-e740c24eb7a90bc771f5976d64d80639ee7576e5.tar.xz |
Codechange: Template DoCommand to automagically reflect the parameters of the command proc.
When finished, this will allow each command handler to take individually
different parameters, obliviating the need for bit-packing.
Diffstat (limited to 'src/command_func.h')
-rw-r--r-- | src/command_func.h | 73 |
1 files changed, 71 insertions, 2 deletions
diff --git a/src/command_func.h b/src/command_func.h index 2a6f0c68a..1c24221cd 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -13,6 +13,7 @@ #include "command_type.h" #include "company_type.h" #include <vector> +#include "tile_map.h" /** * Define a default return value for a failed command. @@ -36,7 +37,6 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID); /** Storage buffer for serialized command data. */ typedef std::vector<byte> CommandDataBuffer; -CommandCost DoCommand(DoCommandFlag flags, Commands cmd, TileIndex tile, uint32 p1, uint32 p2, const std::string &text = {}); bool DoCommandP(Commands cmd, StringID err_message, CommandCallback *callback, TileIndex tile, uint32 p1, uint32 p2, const std::string &text = {}); bool DoCommandP(Commands cmd, StringID err_message, TileIndex tile, uint32 p1, uint32 p2, const std::string &text = {}); @@ -58,12 +58,18 @@ const char *GetCommandName(Commands cmd); Money GetAvailableMoneyForCommand(); bool IsCommandAllowedWhilePaused(Commands cmd); +template <Commands Tcmd> +constexpr CommandFlags GetCommandFlags() +{ + return CommandTraits<Tcmd>::flags; +} + /** * Extracts the DC flags needed for DoCommand from the flags returned by GetCommandFlags * @param cmd_flags Flags from GetCommandFlags * @return flags for DoCommand */ -static inline DoCommandFlag CommandFlagsToDCFlags(CommandFlags cmd_flags) +static constexpr inline DoCommandFlag CommandFlagsToDCFlags(CommandFlags cmd_flags) { DoCommandFlag flags = DC_NONE; if (cmd_flags & CMD_NO_WATER) flags |= DC_NO_WATER; @@ -83,4 +89,67 @@ private: static int _counter; }; + +template<Commands TCmd, typename T> struct CommandHelper; + +class CommandHelperBase { +protected: + static void InternalDoBefore(bool top_level, bool test); + static void InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test); +}; + +/** + * Templated wrapper that exposes the command parameter arguments + * for the various Command::Do/Post calls. + * @tparam Tcmd The command-id to execute. + * @tparam Targs The command parameter types. + */ +template <Commands Tcmd, typename... Targs> +struct CommandHelper<Tcmd, CommandCost(*)(DoCommandFlag, Targs...)> : protected CommandHelperBase { +public: + /** + * This function executes a given command with the parameters from the #CommandProc parameter list. + * Depending on the flags parameter it executes or tests a command. + * + * @note This function is to be called from the StateGameLoop or from within the execution of a Command. + * This function must not be called from the context of a "player" (real person, AI, game script). + * Use ::Post for commands directly triggered by "players". + * + * @param flags Flags for the command and how to execute the command + * @param args Parameters for the command + * @see CommandProc + * @return the cost + */ + static CommandCost Do(DoCommandFlag flags, Targs... args) + { + if constexpr (std::is_same_v<TileIndex, std::tuple_element_t<0, std::tuple<Targs...>>>) { + /* Do not even think about executing out-of-bounds tile-commands. */ + TileIndex tile = std::get<0>(std::make_tuple(args...)); + if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (flags & DC_ALL_TILES) == 0))) return CMD_ERROR; + } + + RecursiveCommandCounter counter{}; + + /* Only execute the test call if it's toplevel, or we're not execing. */ + if (counter.IsTopLevel() || !(flags & DC_EXEC)) { + InternalDoBefore(counter.IsTopLevel(), true); + CommandCost res = CommandTraits<Tcmd>::proc(flags & ~DC_EXEC, args...); + InternalDoAfter(res, flags, counter.IsTopLevel(), true); // Can modify res. + + if (res.Failed() || !(flags & DC_EXEC)) return res; + } + + /* Execute the command here. All cost-relevant functions set the expenses type + * themselves to the cost object at some point. */ + InternalDoBefore(counter.IsTopLevel(), false); + CommandCost res = CommandTraits<Tcmd>::proc(flags, args...); + InternalDoAfter(res, flags, counter.IsTopLevel(), false); + + return res; + } +}; + +template <Commands Tcmd> +using Command = CommandHelper<Tcmd, typename CommandTraits<Tcmd>::ProcType>; + #endif /* COMMAND_FUNC_H */ |