summaryrefslogtreecommitdiff
path: root/src/command_func.h
diff options
context:
space:
mode:
authorMichael Lutz <michi@icosahedron.de>2021-10-30 01:31:46 +0200
committerMichael Lutz <michi@icosahedron.de>2021-12-16 22:28:32 +0100
commite740c24eb7a90bc771f5976d64d80639ee7576e5 (patch)
treeb3c8f0c87419cb11c106ba1cb58d6ae0648beef4 /src/command_func.h
parentc88b104ec662ea80bec89f58aa7ad9d0baac7704 (diff)
downloadopenttd-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.h73
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 */