aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/scripts/Condition.hpp
blob: c390b26d47c8aae0e93e087a30dd92858dd8c121 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#pragma once

#include <ostream>
#include <string>
#include <string_view>
#include <variant>

#include "openvic-simulation/dataloader/NodeTools.hpp"
#include "openvic-simulation/types/EnumBitfield.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"

namespace OpenVic {
   enum class scope_type_t : uint8_t {
      NO_SCOPE    = 0,
      POP         = 1 << 0,
      PROVINCE    = 1 << 1,
      STATE       = 1 << 2,
      COUNTRY     = 1 << 3,
      THIS        = 1 << 4, // Indicator bit for scope switching ("use the THIS scope", not a scope in and of itself)
      FROM        = 1 << 5, // Indicator bit for scope switching ("use the FROM scope", not a scope in and of itself)
      FULL_SCOPE_MASK   = (1 << 6) - 1, // All possible scope bits (including THIS and FROM)
      ALL_SCOPES = POP | PROVINCE | STATE | COUNTRY // All real scopes (without THIS and FROM)
   };

   /* Allows enum types to be used with bitwise operators. */
   template<> struct enable_bitfield<scope_type_t> : std::true_type {};

   /* Returns true if the values have any bit in common. */
   inline constexpr bool share_scope_type(scope_type_t lhs, scope_type_t rhs) {
      return (lhs & rhs) != scope_type_t::NO_SCOPE;
   }

#define BUILD_STRING(entry) \
   if (share_scope_type(value, entry)) { \
      if (type_found) { \
         stream << " | "; \
      } else { \
         type_found = true; \
      } \
      stream << #entry; \
   }

   inline std::ostream& operator<<(std::ostream& stream, scope_type_t value) {
      using enum scope_type_t;

      if (value == NO_SCOPE) {
         return stream << "[NO_SCOPE]";
      }

      bool type_found = false;

      stream << '[';

      BUILD_STRING(COUNTRY);
      BUILD_STRING(STATE);
      BUILD_STRING(PROVINCE);
      BUILD_STRING(POP);
      BUILD_STRING(THIS);
      BUILD_STRING(FROM);

      if (!type_found) {
         stream << "INVALID SCOPE";
      }

      return stream << ']';
   }

#undef BUILD_STRING

   struct ConditionManager;
   struct ConditionScript;
   struct CountryDefinition;
   struct CountryInstance;
   struct ProvinceDefinition;
   struct ProvinceInstance;
   struct Pop;
   struct GoodDefinition;
   struct ProvinceSetModifier;
   using Continent = ProvinceSetModifier;
   struct Condition;
   struct DefinitionManager;
   struct InstanceManager;

   struct ConditionNode {
      friend struct ConditionManager;
      friend struct ConditionScript;

      // std::variant's default constructor sets it to the first type in its parameter list, so for argument_t and scope_t
      // a default-constructed instance represents no_argument_t or no_scope_t.

      struct no_argument_t {};
      struct this_argument_t {};
      struct from_argument_t {};
      using integer_t = int64_t;
      using argument_t = std::variant<
         // No argument
         no_argument_t,
         // Script reference arguments
         this_argument_t, from_argument_t,
         // List argument
         std::vector<ConditionNode>,
         // Value arguments
         bool, std::string, integer_t, fixed_point_t,
         // Game object arguments
         CountryDefinition const*, ProvinceDefinition const*, GoodDefinition const*, Continent const*
      >;

      static constexpr bool is_this_argument(argument_t const& argument) {
         return std::holds_alternative<this_argument_t>(argument);
      }

      static constexpr bool is_from_argument(argument_t const& argument) {
         return std::holds_alternative<from_argument_t>(argument);
      }

      struct no_scope_t {};
      using scope_t = std::variant<
         no_scope_t,
         CountryInstance const*,
         ProvinceInstance const*,
         Pop const*
      >;

      static constexpr bool is_no_scope(scope_t const& scope) {
         return std::holds_alternative<no_scope_t>(scope);
      }

   private:
      Condition const* PROPERTY(condition);
      argument_t PROPERTY(argument);

      ConditionNode(
         Condition const* new_condition = nullptr, argument_t&& new_argument = no_argument_t {}
      );

   public:
      ConditionNode(ConditionNode&&) = default;
      ConditionNode& operator=(ConditionNode&&) = default;

      constexpr bool is_initialised() const {
         return condition != nullptr;
      }

      bool execute(
         InstanceManager const& instance_manager, scope_t const& current_scope, scope_t const& this_scope,
         scope_t const& from_scope
      ) const;
   };

   struct Condition : HasIdentifier {
      friend struct ConditionManager;

      using parse_callback_t = NodeTools::callback_t<
         // bool(definition_manager, current_scope, this_scope, from_scope, node, callback)
         DefinitionManager const&, scope_type_t, scope_type_t, scope_type_t, ast::NodeCPtr,
         NodeTools::callback_t<ConditionNode::argument_t&&>
      >;
      using execute_callback_t = NodeTools::callback_t<
         // bool(instance_manager, current_scope, this_scope, from_scope, argument)
         InstanceManager const&, ConditionNode::scope_t const&, ConditionNode::scope_t const&,
         ConditionNode::scope_t const&, ConditionNode::argument_t const&
      >;

   private:
      parse_callback_t PROPERTY(parse_callback);
      execute_callback_t PROPERTY(execute_callback);

      Condition(
         std::string_view new_identifier,
         parse_callback_t&& new_parse_callback,
         execute_callback_t&& new_execute_callback
      );

   public:
      Condition(Condition&&) = default;
   };

   struct ConditionManager {
   private:
      CaseInsensitiveIdentifierRegistry<Condition> IDENTIFIER_REGISTRY(condition);
      Condition const* PROPERTY(root_condition);

      bool add_condition(
         std::string_view identifier,
         Condition::parse_callback_t&& parse_callback,
         Condition::execute_callback_t&& execute_callback
      );

      template<
         scope_type_t CHANGE_SCOPE = scope_type_t::NO_SCOPE,
         scope_type_t ALLOWED_SCOPES = scope_type_t::ALL_SCOPES,
         bool TOP_SCOPE = false
      >
      static bool _parse_condition_node_list_callback(
         DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope,
         scope_type_t from_scope, ast::NodeCPtr node, NodeTools::callback_t<ConditionNode::argument_t&&> callback
      );

      NodeTools::Callback<Condition const&, ast::NodeCPtr> auto expect_condition_node(
         DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope,
         scope_type_t from_scope, NodeTools::Callback<ConditionNode&&> auto callback
      ) const;

      NodeTools::NodeCallback auto expect_condition_node_list_and_length(
         DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope,
         scope_type_t from_scope, NodeTools::Callback<ConditionNode&&> auto callback,
         NodeTools::LengthCallback auto length_callback, bool top_scope = false
      ) const;

      NodeTools::NodeCallback auto expect_condition_node_list(
         DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope,
         scope_type_t from_scope, NodeTools::Callback<ConditionNode&&> auto callback, bool top_scope = false
      ) const;

   public:
      ConditionManager();

      bool setup_conditions(DefinitionManager const& definition_manager);

      NodeTools::node_callback_t expect_condition_script(
         DefinitionManager const& definition_manager, scope_type_t initial_scope, scope_type_t this_scope,
         scope_type_t from_scope, NodeTools::callback_t<ConditionNode&&> callback
      ) const;
   };
}