Add class 'SimpleDynamic' to base-writer, migrate AnimValue to use it

This commit is contained in:
2023-10-17 19:34:50 -04:00
parent edea43839f
commit 5373182a59
4 changed files with 166 additions and 199 deletions

View File

@@ -9,95 +9,12 @@
#include <cmath>
#include <cstdlib>
static const char *vtname(AnimValueType vt) {
switch (vt) {
case T_UNINITIALIZED: return "uninitialized";
case T_BOOLEAN: return "boolean";
case T_NUMBER: return "number";
case T_STRING: return "string";
case T_XYZ: return "xyz";
default: return "unknown";
}
}
static uint64_t hash_encstep(uint64_t prev, std::string_view s) {
return util::hash_string(util::HashValue(123, prev), s).first;
}
void AnimValue::set_boolean(bool b) {
type = T_BOOLEAN;
str.clear();
xyz = (b ? 1.0:0.0);
}
void AnimValue::set_number(double n) {
type = T_NUMBER;
str.clear();
xyz = util::DXYZ(n, 0, 0);
}
void AnimValue::set_xyz(const util::DXYZ &v) {
type = T_XYZ;
str.clear();
xyz = v;
}
void AnimValue::set_string(std::string_view sv) {
type = T_STRING;
str = sv;
xyz = 0.0;
}
bool AnimValue::get_boolean() const {
if (type != T_BOOLEAN) return false;
return (xyz.x == 1.0);
}
double AnimValue::get_number() const {
if (type != T_NUMBER) return 0.0;
return xyz.x;
}
const util::DXYZ &AnimValue::get_xyz() const {
static util::DXYZ zero;
if (type != T_XYZ) return zero;
return xyz;
}
std::string_view AnimValue::get_string() const {
if (type != T_STRING) return std::string_view("");
return std::string_view(str);
}
void AnimValue::copy_value(const AnimValue &other) {
type = other.type;
str = other.str;
xyz = other.xyz;
}
static void encode_value(StreamBuffer *sb, const AnimValue *v) {
sb->write_uint8(uint8_t(v->type));
switch(v->type) {
case T_NUMBER: sb->write_double(v->xyz.x); break;
case T_BOOLEAN: sb->write_bool(v->xyz.x == 1.0); break;
case T_XYZ: sb->write_dxyz(v->xyz); break;
case T_STRING: sb->write_string(v->str); break;
default: assert(false);
}
}
static void decode_value(StreamBuffer *sb, AnimValue *v) {
AnimValueType type = AnimValueType(sb->read_uint8());
switch (type) {
case T_NUMBER: v->set_number(sb->read_double()); break;
case T_BOOLEAN: v->set_boolean(sb->read_bool()); break;
case T_XYZ: v->set_xyz(sb->read_dxyz()); break;
case T_STRING: v->set_string(sb->read_string()); break;
default: assert(false);
}
}
// Parse a value. This is meant for unit testing only. The
// parser isn't powerful enough to express all possible values.
static void parse_value(std::string_view vstr, AnimValue *v) {
@@ -122,7 +39,7 @@ static void parse_value(std::string_view vstr, AnimValue *v) {
double x = std::atof(parts[0].c_str());
double y = std::atof(parts[1].c_str());
double z = std::atof(parts[2].c_str());
v->set_xyz(util::DXYZ(x,y,z));
v->set_dxyz(util::DXYZ(x,y,z));
return;
}
// If it doesn't parse as any of the above, it's a string.
@@ -149,7 +66,7 @@ static AnimValue parse_anim_value(LuaCoreStack &LS, LuaSlot val, LuaSlot tmp) {
LS.rawget(tmp, val, 3);
if (!LS.isnumber(tmp)) return result;
xyz.z = LS.cknumber(tmp);
result.set_xyz(xyz);
result.set_dxyz(xyz);
}
return result;
}
@@ -158,32 +75,6 @@ void AnimState::set_persistent(const eng::string &name) {
map_[name].persistent = true;
}
bool AnimState::get_boolean(const eng::string &name) {
auto iter = map_.find(name);
if (iter == map_.end()) return false;
return iter->second.get_boolean();
}
double AnimState::get_number(const eng::string &name) {
auto iter = map_.find(name);
if (iter == map_.end()) return 0.0;
return iter->second.get_number();
}
util::DXYZ AnimState::get_xyz(const eng::string &name) {
static util::DXYZ zero;
auto iter = map_.find(name);
if (iter == map_.end()) return zero;
return iter->second.get_xyz();
}
std::string_view AnimState::get_string(const eng::string &name) {
auto iter = map_.find(name);
if (iter == map_.end()) return std::string_view("");
return iter->second.get_string();
}
void AnimState::set_boolean(const eng::string &name, bool v) {
AnimValue &value = map_[name];
value.set_boolean(v);
@@ -194,9 +85,9 @@ void AnimState::set_number(const eng::string &name, double v) {
value.set_number(v);
}
void AnimState::set_xyz(const eng::string &name, const util::DXYZ &v) {
void AnimState::set_dxyz(const eng::string &name, const util::DXYZ &v) {
AnimValue &value = map_[name];
value.set_xyz(v);
value.set_dxyz(v);
}
void AnimState::set_string(const eng::string &name, std::string_view v) {
@@ -220,10 +111,10 @@ void AnimState::print_debug_string(eng::ostringstream &oss) {
oss << ":";
}
switch (value.type) {
case T_NUMBER: oss << value.xyz.x; break;
case T_BOOLEAN: oss << ((value.xyz.x == 1.0) ? "true":"false"); break;
case T_XYZ: oss << value.xyz; break;
case T_STRING: oss << value.str; break;
case SimpleDynamicTag::NUMBER: oss << value.x; break;
case SimpleDynamicTag::BOOLEAN: oss << ((value.x == 1.0) ? "true":"false"); break;
case SimpleDynamicTag::VECTOR: oss << value.x << "," << value.y << "," << value.z; break;
case SimpleDynamicTag::STRING: oss << value.s; break;
default: assert(false);
}
first = false;
@@ -243,7 +134,7 @@ eng::string AnimState::encode() const {
const AnimValue &value = pair.second;
sb.write_string(name);
sb.write_bool(value.persistent);
encode_value(&sb, &value);
sb.write_simple_dynamic(value);
}
return sb.read_entire_contents();
}
@@ -253,26 +144,25 @@ void AnimState::decode(std::string_view s) {
StreamBuffer sb(s);
while (!sb.empty()) {
eng::string name = sb.read_string();
bool persistent = sb.read_bool();
AnimValue &value = map_[name];
value.persistent = persistent;
decode_value(&sb, &value);
value.persistent = sb.read_bool();
sb.read_simple_dynamic(&value);
}
}
void AnimState::decode_persistent(std::string_view s) {
map_.clear();
StreamBuffer sb(s);
AnimValue dummy;
SimpleDynamic dummy;
while (!sb.empty()) {
eng::string name = sb.read_string();
bool persistent = sb.read_bool();
if (persistent) {
AnimValue &value = map_[name];
value.persistent = persistent;
decode_value(&sb, &value);
sb.read_simple_dynamic(&value);
} else {
decode_value(&sb, &dummy);
sb.read_simple_dynamic(&dummy);
}
}
}
@@ -280,7 +170,7 @@ void AnimState::decode_persistent(std::string_view s) {
eng::string AnimState::add_default(const eng::string &name, const AnimValue &def, const AnimState *other) {
AnimValue &value = map_[name];
value.persistent = true;
if (value.type == T_UNINITIALIZED) {
if (value.type == SimpleDynamicTag::UNINITIALIZED) {
if (other != nullptr) {
auto otheriter = other->map_.find(name);
if (otheriter != other->map_.end()) {
@@ -294,7 +184,7 @@ eng::string AnimState::add_default(const eng::string &name, const AnimValue &def
return "";
}
if (value.type != def.type) {
return util::ss("Animation key ", name, " must be a ", vtname(def.type));
return util::ss("Animation key ", name, " must be a ", def.type_name());
}
return "";
}
@@ -303,7 +193,7 @@ eng::string AnimState::add_defaults(const AnimState *other) {
eng::string err;
AnimValue defval;
defval.set_xyz(util::DXYZ(0,0,0));
defval.set_dxyz(util::DXYZ(0,0,0));
err = add_default("xyz", defval, other);
if (!err.empty()) return err;
@@ -341,13 +231,13 @@ eng::string AnimState::apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist
return "in animation key-value pairs, key must be a string.";
}
AnimValue parsedvalue = parse_anim_value(LS, val, tmp);
if (parsedvalue.type == T_UNINITIALIZED) {
if (parsedvalue.type == SimpleDynamicTag::UNINITIALIZED) {
return "in animation key-value pairs, val must be number, string, boolean, or xyz";
}
eng::string name = LS.ckstring(key);
AnimValue &mapentry = map_[name];
if ((mapentry.type != T_UNINITIALIZED) && (mapentry.type != parsedvalue.type)) {
return util::ss("animation '", name, "' must be a ", vtname(mapentry.type));
if ((mapentry.type != SimpleDynamicTag::UNINITIALIZED) && (mapentry.type != parsedvalue.type)) {
return util::ss("animation '", name, "' must be a ", mapentry.type_name());
}
mapentry.copy_value(parsedvalue);
if (setpersist) mapentry.persistent = true;
@@ -363,17 +253,17 @@ void AnimState::to_lua(LuaCoreStack &LS0, LuaSlot tab, bool persistent) {
if (pair.second.persistent != persistent) continue;
LS.set(name, pair.first);
const AnimValue &value = pair.second;
if (value.type == T_BOOLEAN) {
LS.set(val, value.get_boolean());
} else if (value.type == T_NUMBER) {
LS.set(val, value.get_number());
} else if (value.type == T_STRING) {
LS.set(val, value.get_string());
} else if (value.type == T_XYZ) {
if (value.type == SimpleDynamicTag::BOOLEAN) {
LS.set(val, (value.x == 1.0));
} else if (value.type == SimpleDynamicTag::NUMBER) {
LS.set(val, value.x);
} else if (value.type == SimpleDynamicTag::STRING) {
LS.set(val, std::string_view(value.s));
} else if (value.type == SimpleDynamicTag::VECTOR) {
LS.newtable(val);
LS.rawset(val, 1, value.get_xyz().x);
LS.rawset(val, 2, value.get_xyz().y);
LS.rawset(val, 3, value.get_xyz().z);
LS.rawset(val, 1, value.x);
LS.rawset(val, 2, value.y);
LS.rawset(val, 3, value.z);
}
LS.rawset(tab, name, val);
}
@@ -407,14 +297,14 @@ void AnimCoreState::decode(std::string_view s) {
plane.clear();
xyz = 0.0;
StreamBuffer sb(s);
AnimValue value;
SimpleDynamic value;
while (!sb.empty()) {
eng::string name = sb.read_string();
bool persistent = sb.read_bool();
decode_value(&sb, &value);
sb.read_simple_dynamic(&value);
if (persistent) {
if ((name == "xyz") && (value.type == T_XYZ)) xyz = value.xyz;
if ((name == "plane") && (value.type == T_STRING)) plane = value.str;
if ((name == "xyz") && (value.type == SimpleDynamicTag::VECTOR)) xyz = util::DXYZ(value.x, value.y, value.z);
if ((name == "plane") && (value.type == SimpleDynamicTag::STRING)) plane = value.s;
}
}
}
@@ -642,28 +532,28 @@ LuaDefine(unittests_animqueue, "", "some unit tests") {
// Test AnimState simple setters.
astate.set_string("color", "blue");
astate.set_xyz("xyz", util::DXYZ(1,2,3));
astate.set_dxyz("xyz", util::DXYZ(1,2,3));
astate.set_number("half", 0.5);
astate.set_boolean("nice", true);
LuaAssertStrEq(L, astate.debug_string(), "color:blue half:0.5 nice:true xyz:1,2,3");
// Test AnimState simple getters.
LuaAssert(L, astate.get_string("color") == "blue");
LuaAssert(L, astate.get_xyz("xyz") == util::DXYZ(1,2,3));
LuaAssert(L, astate.get_number("half") == 0.5);
LuaAssert(L, astate.get_boolean("nice") == true);
// // Test AnimState simple getters.
// LuaAssert(L, astate.get_string("color") == "blue");
// LuaAssert(L, astate.get_xyz("xyz") == util::DXYZ(1,2,3));
// LuaAssert(L, astate.get_number("half") == 0.5);
// LuaAssert(L, astate.get_boolean("nice") == true);
// Test AnimState simple getters on nonexistent data.
LuaAssert(L, astate.get_string("q") == "");
LuaAssert(L, astate.get_xyz("q") == util::DXYZ(0,0,0));
LuaAssert(L, astate.get_number("q") == 0.0);
LuaAssert(L, astate.get_boolean("q") == false);
// // Test AnimState simple getters on nonexistent data.
// LuaAssert(L, astate.get_string("q") == "");
// LuaAssert(L, astate.get_xyz("q") == util::DXYZ(0,0,0));
// LuaAssert(L, astate.get_number("q") == 0.0);
// LuaAssert(L, astate.get_boolean("q") == false);
// Test AnimState simple getters on wrong-type data.
LuaAssert(L, astate.get_string("half") == "");
LuaAssert(L, astate.get_xyz("half") == util::DXYZ(0,0,0));
LuaAssert(L, astate.get_number("color") == 0.0);
LuaAssert(L, astate.get_boolean("color") == false);
// // Test AnimState simple getters on wrong-type data.
// LuaAssert(L, astate.get_string("half") == "");
// LuaAssert(L, astate.get_xyz("half") == util::DXYZ(0,0,0));
// LuaAssert(L, astate.get_number("color") == 0.0);
// LuaAssert(L, astate.get_boolean("color") == false);
// Test AnimState persistence manipulation.
astate.set_persistent("color");

View File

@@ -93,6 +93,7 @@
#include "wrap-deque.hpp"
#include "wrap-unordered-map.hpp"
#include "base-writer.hpp"
#include "luastack.hpp"
#include "streambuffer.hpp"
#include "debugcollector.hpp"
@@ -101,36 +102,15 @@
#include <cassert>
#include <ostream>
enum AnimValueType {
T_STRING,
T_NUMBER,
T_BOOLEAN,
T_XYZ,
T_UNINITIALIZED
};
struct AnimValue {
AnimValue() : persistent(false), type(T_UNINITIALIZED) {}
struct AnimValue : public SimpleDynamic {
bool persistent;
AnimValueType type;
eng::string str;
util::DXYZ xyz;
// 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);
AnimValue() { persistent = false; }
// 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;
// Copy the value from another animvalue. Don't copy the persistent flag.
void copy_value(const AnimValue &other);
void set_dxyz(const util::DXYZ &xyz) {
type = SimpleDynamicTag::VECTOR;
s.clear(); x = xyz.x; y = xyz.y; z = xyz.z;
}
};
@@ -158,20 +138,12 @@ public:
//
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_dxyz(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.

View File

@@ -18,7 +18,7 @@ void World::tangible_clear_plane_and_xyz(int64_t id, const eng::string &plane, c
assert(t != nullptr);
AnimState state;
state.set_string("plane", plane);
state.set_xyz("xyz", xyz);
state.set_dxyz("xyz", xyz);
state.set_persistent("plane");
state.set_persistent("xyz");
t->anim_queue_.clear(state);
@@ -30,7 +30,7 @@ void World::tangible_walkto(int64_t id, float x, float y) {
assert(t != nullptr);
AnimState state = t->anim_queue_.get_final_persistent();
state.set_string("action", "walkto");
state.set_xyz("xyz", util::DXYZ(x,y,0.0));
state.set_dxyz("xyz", util::DXYZ(x,y,0.0));
t->anim_queue_.add(state);
}