+#pragma once
+
+#include <string>
+#include <sstream>
+#include <vector>
+#include <ctgmath>
+#include <type_traits>
+#include "irrlichttypes_bloated.h"
+#include "tileanimation.h"
+#include "mapnode.h"
+#include "util/serialize.h"
+#include "util/numeric.h"
+
+// This file defines the particle-related structures that both the server and
+// client need. The ParticleManager and rendering is in client/particles.h
+
+namespace ParticleParamTypes
+{
+ template <bool cond, typename T>
+ using enableIf = typename std::enable_if<cond, T>::type;
+ // std::enable_if_t does not appear to be present in GCC????
+ // std::is_enum_v also missing. wtf. these are supposed to be
+ // present as of c++14
+
+ template<typename T> using BlendFunction = T(float,T,T);
+ #define DECL_PARAM_SRZRS(type) \
+ void serializeParameterValue (std::ostream& os, type v); \
+ void deSerializeParameterValue(std::istream& is, type& r);
+ #define DECL_PARAM_OVERLOADS(type) DECL_PARAM_SRZRS(type) \
+ type interpolateParameterValue(float fac, const type a, const type b); \
+ type pickParameterValue (float* facs, const type a, const type b);
+
+ DECL_PARAM_OVERLOADS(u8); DECL_PARAM_OVERLOADS(s8);
+ DECL_PARAM_OVERLOADS(u16); DECL_PARAM_OVERLOADS(s16);
+ DECL_PARAM_OVERLOADS(u32); DECL_PARAM_OVERLOADS(s32);
+ DECL_PARAM_OVERLOADS(f32);
+ DECL_PARAM_OVERLOADS(v2f);
+ DECL_PARAM_OVERLOADS(v3f);
+
+ /* C++ is a strongly typed language. this means that enums cannot be implicitly
+ * cast to integers, as they can be in C. while this may sound good in principle,
+ * it means that our normal serialization functions cannot be called on
+ * enumerations unless they are explicitly cast to a particular type first. this
+ * is problematic, because in C++ enums can have any integral type as an underlying
+ * type, and that type would need to be named everywhere an enumeration is
+ * de/serialized.
+ *
+ * this is obviously not cool, both in terms of writing legible, succinct code,
+ * and in terms of robustness: the underlying type might be changed at some point,
+ * e.g. if a bitmask gets too big for its britches. we could use an equivalent of
+ * `std::to_underlying(value)` everywhere we need to deal with enumerations, but
+ * that's hideous and unintuitive. instead, we supply the following functions to
+ * transparently map enumeration types to their underlying values. */
+
+ template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
+ void serializeParameterValue(std::ostream& os, E k) {
+ serializeParameterValue(os, (std::underlying_type_t<E>)k);
+ }