/////////////////////////////////////////////////////////////////// // // ANIMATION QUEUES // // An animation queue is a fifo queue of animation steps. New animations are // pushed on the back, and old ones are popped from the front. // // An animation step has an "action" which is usually the name of an animation, // or it's a special token like "walk" or "warp." Each animation step shows the // resulting AnimState that the player finds himself in after executing the // action. // // The first step in an animation queue always has id=0 and action="". This step // represents the initial state of the sprite before any animations or // movements. // // To add new items to the AnimQueue, use this process: first, call add(id, // action). This adds a new step to the queue. Then, call set_xyz, set_facing, // set_plane, or an other setter. These setters are meant to only be used // immediately after calling 'add' to populate the new step. // // VERSION NUMBERS // // The version number field: if the version number in the synchronous model is // equal to the version number in the master model, then the two animqueues are // guaranteed to be equal. Here's how we achieve that invariant: // // * In the master model, the version number starts at 1 and is auto-incremented // every time the animation queue is mutated. // // * In the synchronous model, the version number is set to zero every time the // animation queue is mutated. Note that master version numbers are never // zero. Applying a patch also sets the version number to zero. // // * Serializing and deserializing causes the version number to be saved and // restored in both master and synchronous models. // // * The routine 'update_version' should be called after difference // transmission. This copies the version number from the master to the // synchronous model. This is guaranteed to be safe because we just finished // difference transmission, therefore, the queues should match. // /////////////////////////////////////////////////////////////////// #ifndef ANIMQUEUE_HPP #define ANIMQUEUE_HPP #include "wrap-set.hpp" #include "wrap-string.hpp" #include "wrap-deque.hpp" #include "wrap-unordered-map.hpp" #include "wrap-ostream.hpp" #include "streambuffer.hpp" #include "debugcollector.hpp" #include "util.hpp" #include class AnimStep { friend class AnimQueue; public: enum { HAS_FACING = 1, HAS_X = 2, HAS_Y = 4, HAS_Z = 8, HAS_XYZ = 14, HAS_GRAPHIC = 16, HAS_PLANE = 32, HAS_EVERYTHING = 63, }; AnimStep(); ~AnimStep(); int64_t id() const { return id_; } int bits() const { return bits_; } const eng::string &action() const { return action_; } double facing() const { return facing_; } float x() const { return xyz_.x; } float y() const { return xyz_.y; } float z() const { return xyz_.z; } const util::XYZ &xyz() const { return xyz_; } const eng::string &graphic() const { return graphic_; } const eng::string &plane() const { return plane_; } bool has_facing() const { return bits_ & HAS_FACING; } bool has_x() const { return bits_ & HAS_X; } bool has_y() const { return bits_ & HAS_Y; } bool has_z() const { return bits_ & HAS_Z; } bool has_xyz() const { return (bits_ & HAS_XYZ) == HAS_XYZ; } bool has_graphic() const { return bits_ & AnimStep::HAS_GRAPHIC; } bool has_plane() const { return bits_ & AnimStep::HAS_PLANE; } void set_action(const eng::string &action); void set_facing(float f); void set_x(float f); void set_y(float f); void set_z(float z); void set_xyz(const util::XYZ &xyz); void set_graphic(const eng::string &g); void set_plane(const eng::string &p); void clear(); // ExactlyEqual compares all fields. bool exactly_equal(const AnimStep &other) const; // LogicallyEqual only compares fields whose HAS_XXX bits are set. bool logically_equal(const AnimStep &other) const; // StateEqual is true if the plane, graphic, facing, and xyz match. bool state_equal(const AnimStep &other) const; // read/write the step using a stream buffer. // void write_into(StreamBuffer *sb) const; void read_from(StreamBuffer *sb); // Create an AnimStep from a lua table. // // Lua stack must contain a table, which may contain: // action: "action" // facing: 0.0 - 360.0 // x: x-coordinate // y: y-coordinate // z: z-coordinate // graphic: "graphic" // plane: "plane" // // qback: the animation queue back, from which relative // moves are computed. // void from_lua(lua_State *L, int idx, bool ignex, const AnimStep &qback); // Make this step into a first-step of an anim queue. void keep_state_only(); // For any values that are unchanged in this step, // echo the values of the previous step. void echo(const AnimStep &prev); // Verify that this step echoes the previous step. bool echoes(const AnimStep &prev) const; // Convert to a string for debugging purposes. eng::string debug_string() const; // Convert a string to an animstep (for testing only). bool from_string(const eng::string &s); private: int64_t id_; int16_t bits_; eng::string action_; float facing_; util::XYZ xyz_; eng::string graphic_; eng::string plane_; void from_lua_store_string(lua_State *L, int idx, eng::string *target, int16_t bits, const char *name); void from_lua_store_number(lua_State *L, int idx, float *target, float offset, int16_t bits, const char *name); }; class AnimQueue { public: // World type determines whether versions increment or autozero AnimQueue(util::WorldType wt); // Simple getters. const AnimStep &nth(int n) const { return steps_[n]; } size_t size() const { return steps_.size(); } int32_t size_limit() const { return size_limit_; } int64_t version_number() const { return version_number_; } bool version_identical(const AnimQueue &aq) const; // Return true if the size limit and steps are identical. bool size_and_steps_equal(const AnimQueue &aq) const; // Mutator to create a new step. void add(int64_t id, const AnimStep &step); // Clear and set the plane. void clear(const eng::string &plane); // Serialize or deserialize to a StreamBuffer // // Caution: version numbers are not stored. On deserialize, // version number is set to zero. // void serialize(StreamBuffer *sb) const; void deserialize(StreamBuffer *sb); // Difference transmission bool diff(const AnimQueue &auth, StreamBuffer *sb) const; void patch(StreamBuffer *sb, DebugCollector *dbc); void update_version(const AnimQueue &auth); // Get the final resting place after all animations are complete. const AnimStep &back() const; public: //////////////////////////////////////////////////////////////////////////// // // TESTING SUPPORT // // The following functions are not designed to be useful for production // code, they're designed to be helpful for unit testing. // //////////////////////////////////////////////////////////////////////////// // Change the size limit. void full_clear_and_set_limit(int szl); void set_limit(int szl); // Make sure the invariants are preserved. bool valid() const; // Convert to a string for debugging purposes. eng::string steps_debug_string() const; eng::string full_debug_string() const; // Convert to a private: bool version_autoinc_; int32_t size_limit_; eng::deque steps_; int64_t version_number_; // Called whenever the animation queue is mutated. // serialization and deserialization void mutated(); }; #endif // ANIMQUEUE_HPP