Redesign of animation queue for unreal, add get_tangibles_near to drivenengine

This commit is contained in:
2023-07-24 17:19:25 -04:00
parent 4da86e6f89
commit 87aa47b96d
19 changed files with 1406 additions and 980 deletions

View File

@@ -5,40 +5,26 @@
// 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.
// An animation step is a set of key-value pairs, where each key is an
// identifier, and each value is either a number, a boolean, an XYZ coordinate,
// or a string. A key-value pair can be either persistent or nonpersistent.
// So a typical animation step might be:
//
// 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.
// action=walk [nonpersistent]
// xyz=3,4,5 [persistent]
// facing=320 [persistent]
// plane=earth [persistent]
//
// 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.
// Persistent values are retained from one animation step to the next,
// nonpersistent values exist for one animation step only.
//
// VERSION NUMBERS
// Animation steps are stored encoded as strings, which is convenient for
// passing the data to unreal, but it means that the animation step has to be
// decoded whenever you want access to the key-value pairs.
//
// 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.
// Each animation step has a hash value. The hash value is generated
// by mixing the hash value of the previous step with the hash value
// of the encoded string of key-value pairs.
//
///////////////////////////////////////////////////////////////////
@@ -50,6 +36,7 @@
#include "wrap-deque.hpp"
#include "wrap-unordered-map.hpp"
#include "luastack.hpp"
#include "streambuffer.hpp"
#include "debugcollector.hpp"
#include "util.hpp"
@@ -57,184 +44,226 @@
#include <cassert>
#include <ostream>
class AnimStep : public eng::nevernew {
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,
};
enum AnimValueType {
T_STRING,
T_NUMBER,
T_BOOLEAN,
T_XYZ,
T_UNINITIALIZED
};
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"
//
// aqback: the animation queue back, from which relative
// moves are computed.
//
void configure(LuaKeywordParser &kp, const AnimStep &aqback);
// Make this step into a first-step of an anim queue.
void keep_state_only();
struct AnimValue {
AnimValue() : persistent(false), type(T_UNINITIALIZED) {}
// For any values that are unchanged in this step,
// echo the values of the previous step.
void echo(const AnimStep &prev);
bool persistent;
AnimValueType type;
eng::string str;
util::DXYZ xyz;
// Verify that this step echoes the previous step.
bool echoes(const AnimStep &prev) const;
// These set the type, str, and xyz fields in a consistent way.
void set_boolean(bool b);
void set_number(double n);
void set_xyz(const util::DXYZ &xyz);
void set_string(std::string_view s);
// Convert to a string for debugging purposes.
eng::string debug_string() const;
// The get the type, str, and xyz fields in a consistent way.
bool get_boolean() const;
double get_number() const;
const util::DXYZ &get_xyz() const;
std::string_view get_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 config_store_string(lua_State *L, int idx, eng::string *target, int16_t bits, const char *name);
void config_store_number(lua_State *L, int idx, float *target, float offset, int16_t bits, const char *name);
// Copy the value from another animvalue. Don't copy the persistent flag.
void copy_value(const AnimValue &other);
};
class AnimQueue : public eng::nevernew {
public:
// World type determines whether versions increment or autozero
AnimQueue(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;
class AnimState
{
private:
using Map = eng::map<eng::string, AnimValue>;
Map map_;
// 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
// Set the default value, internal
//
// Caution: version numbers are not stored. On deserialize,
// version number is set to zero.
eng::string add_default(const eng::string &name, const AnimValue &v, const AnimState *other);
public:
// Clear everything
//
void clear() { map_.clear(); }
// Set the persistent flag on a single variable.
// If the variable isn't present, add it.
//
void set_persistent(const eng::string &name);
// Return if it contains a value for the specified name.
//
bool contains(const eng::string &name) { return map_.find(name) != map_.end(); }
// Get the value of a specific variable.
// If the variable isn't present, return a default value.
//
bool get_boolean(const eng::string &name);
double get_number(const eng::string &name);
util::DXYZ get_xyz(const eng::string &name);
std::string_view get_string(const eng::string &name);
// Set a single variable to a value of a specified type.
// If the variable isn't present, add it.
//
void set_boolean(const eng::string &name, bool v);
void set_number(const eng::string &name, double v);
void set_xyz(const eng::string &name, const util::DXYZ &value);
void set_string(const eng::string &name, std::string_view value);
// Print a debug string into the stringstream.
//
void print_debug_string(eng::ostringstream &oss);
// Return the debug string.
eng::string debug_string();
// Constructs an empty state.
//
AnimState() {}
// Convert to an encoded string.
//
eng::string encode() const;
// Decode an encoded string.
//
void decode(std::string_view enc);
// Decode an encoded string, discarding non-persistent data.
//
void decode_persistent(std::string_view enc);
// Add default values for all builtin persistent variables.
//
// For each builtin default (plane, xyz, facing, bp, model)
//
// - Will generate an error if a value is already present,
// but the present value is of the wrong type.
//
// - If 'other' is not nullptr, then we look in 'other' for a default
// value. If a valid value of the correct type is present, it is
// used as the default value.
//
// - If no default value can be found in 'other', then a hardwired
// default value is provided.
//
eng::string add_defaults(const AnimState *other);
// Apply a configuration from a lua table.
//
// If a key already exists in the state, then type type from the table
// must match the type from the existing state.
//
eng::string apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist);
// Convert an animstate to a lua table.
//
// You can either convert the persistent key-value pairs, or the
// nonpersistent. So you'll need two lua tables to store both.
//
void to_lua(LuaCoreStack &LS, LuaSlot tab, bool persistent);
// Parse a string, for unit testing.
//
void parse(std::string_view s);
void clear_and_parse(std::string_view s);
// Constructor from an encoded string.
//
AnimState(std::string_view s) { decode(s); }
};
struct AnimCoreState
{
util::DXYZ xyz;
eng::string plane;
void decode(std::string_view enc);
};
class AnimQueue : public eng::nevernew {
private:
struct Step {
Step() { hash=0; }
Step(const eng::string &e, uint64_t h) : encoding(e), hash(h) {}
eng::string encoding;
uint64_t hash;
};
public:
// Construct an empty animation queue.
// clears the state to a valid state.
//
AnimQueue();
// Size limit.
//
int32_t size_limit() const { return size_limit_; }
// Clear and set the initial state.
//
void clear();
void clear(const AnimState &initial);
// Set the size limit. Must be 2-250
//
void set_limit(int n);
// Add an animation step.
//
// Note: add does not automatically compose the step with the previous
// step, you have to do that yourself.
//
void add(const AnimState &state);
// Serialize or deserialize to a StreamBuffer
//
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:
////////////////////////////////////////////////////////////////////////////
// Check for exactly equal. This does the full check of all
// fields, it is used exclusively for debugging.
//
// TESTING SUPPORT
//
// The following functions are not designed to be useful for production
// code, they're designed to be helpful for unit testing.
//
////////////////////////////////////////////////////////////////////////////
bool exactly_equal(const AnimQueue &aq) const;
// 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;
// Check for exactly equal (fast). Compares the size, limit,
// and hash of the last entry. If these are equal, then the whole
// thing should be equal.
//
bool exactly_equal_fast(const AnimQueue &aq) const;
// Convert to a string for debugging purposes.
// Debug strings.
//
eng::string steps_debug_string() const;
eng::string full_debug_string() const;
// Get the final entry, xyz and plane only.
//
AnimCoreState get_final_core_state() const;
// Get the final entry, all persistent variables.
//
AnimState get_final_persistent() const;
// Get the final entry, everything persistent and non-persistent.
//
AnimState get_final_everything() const;
// Convert to a
private:
bool version_autoinc_;
int32_t size_limit_;
eng::deque<AnimStep> steps_;
int64_t version_number_;
// Called whenever the animation queue is mutated.
// serialization and deserialization
void mutated();
int size_limit_;
eng::deque<Step> steps_;
};
#endif // ANIMQUEUE_HPP