Fix blank animqueues, and add facing=math.auto to animate
This commit is contained in:
@@ -10,6 +10,12 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
||||||
|
util::SharedStdString AnimQueue::blankqueue_;
|
||||||
|
|
||||||
|
void AnimQueue::initialize_module() {
|
||||||
|
AnimQueue queue;
|
||||||
|
blankqueue_ = queue.get_encoded_queue();
|
||||||
|
}
|
||||||
|
|
||||||
static uint64_t hash_encstep(uint64_t prev, std::string_view s) {
|
static uint64_t hash_encstep(uint64_t prev, std::string_view s) {
|
||||||
return util::hash_string(util::HashValue(123, prev), s).first;
|
return util::hash_string(util::HashValue(123, prev), s).first;
|
||||||
@@ -67,6 +73,10 @@ static AnimValue parse_anim_value(LuaCoreStack &LS, LuaSlot val, LuaSlot tmp) {
|
|||||||
if (!LS.isnumber(tmp)) return result;
|
if (!LS.isnumber(tmp)) return result;
|
||||||
xyz.z = LS.cknumber(tmp);
|
xyz.z = LS.cknumber(tmp);
|
||||||
result.set_dxyz(xyz);
|
result.set_dxyz(xyz);
|
||||||
|
} else if (LS.rawequal(val, LuaToken("auto"))) {
|
||||||
|
result.set_auto();
|
||||||
|
} else {
|
||||||
|
result.set_uninitialized();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -111,6 +121,8 @@ void AnimState::print_debug_string(eng::ostringstream &oss) {
|
|||||||
oss << ":";
|
oss << ":";
|
||||||
}
|
}
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
|
case SimpleDynamicTag::UNINITIALIZED: oss << "UNINITIALIZED"; break;
|
||||||
|
case SimpleDynamicTag::AUTO: oss << "AUTO"; break;
|
||||||
case SimpleDynamicTag::NUMBER: oss << value.x; break;
|
case SimpleDynamicTag::NUMBER: oss << value.x; break;
|
||||||
case SimpleDynamicTag::BOOLEAN: oss << ((value.x == 1.0) ? "true":"false"); 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::VECTOR: oss << value.x << "," << value.y << "," << value.z; break;
|
||||||
@@ -205,46 +217,91 @@ eng::string AnimState::add_defaults(const AnimState *other) {
|
|||||||
err = add_default("facing", defval, other);
|
err = add_default("facing", defval, other);
|
||||||
if (!err.empty()) return err;
|
if (!err.empty()) return err;
|
||||||
|
|
||||||
defval.set_string("stdbp");
|
defval.set_string("unknown");
|
||||||
err = add_default("bp", defval, other);
|
err = add_default("bp", defval, other);
|
||||||
if (!err.empty()) return err;
|
if (!err.empty()) return err;
|
||||||
|
|
||||||
defval.set_string("stdmodel");
|
|
||||||
err = add_default("model", defval, other);
|
|
||||||
if (!err.empty()) return err;
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
eng::string AnimState::apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist) {
|
eng::string AnimState::from_lua(LuaCoreStack &LS0, LuaSlot tab, bool persistent, bool allowauto) {
|
||||||
LuaVar key, val, tmp;
|
LuaVar key, val, tmp;
|
||||||
LuaExtStack LS(LS0.state(), key, val, tmp);
|
LuaExtStack LS(LS0.state(), key, val, tmp);
|
||||||
util::DXYZ xyz;
|
util::DXYZ xyz;
|
||||||
|
|
||||||
|
clear();
|
||||||
if (!LS.istable(tab)) {
|
if (!LS.istable(tab)) {
|
||||||
return "An animstate must be a table.";
|
return "A lua animstate must be a table.";
|
||||||
}
|
}
|
||||||
LS.set(key, LuaNil);
|
LS.set(key, LuaNil);
|
||||||
while (LS.next(tab, key, val)) {
|
while (LS.next(tab, key, val)) {
|
||||||
if (!LS.isstring(key)) {
|
if (!LS.isstring(key)) {
|
||||||
return "in animation key-value pairs, key must be a string.";
|
return "in animation key-value pairs, key must be a string.";
|
||||||
}
|
}
|
||||||
|
eng::string name = LS.ckstring(key);
|
||||||
|
if (!LS.valididentifier(name)) {
|
||||||
|
return "in animation key-value pairs, key must be a valid lua identifier.";
|
||||||
|
}
|
||||||
AnimValue parsedvalue = parse_anim_value(LS, val, tmp);
|
AnimValue parsedvalue = parse_anim_value(LS, val, tmp);
|
||||||
if (parsedvalue.type == SimpleDynamicTag::UNINITIALIZED) {
|
if (parsedvalue.type == SimpleDynamicTag::UNINITIALIZED) {
|
||||||
return "in animation key-value pairs, val must be number, string, boolean, or xyz";
|
return "in animation key-value pairs, value must be number, string, boolean, or xyz";
|
||||||
|
}
|
||||||
|
if ((parsedvalue.type == SimpleDynamicTag::AUTO) && !allowauto) {
|
||||||
|
return "in animation key-value pairs, value must not be AUTO here.";
|
||||||
}
|
}
|
||||||
eng::string name = LS.ckstring(key);
|
|
||||||
AnimValue &mapentry = map_[name];
|
AnimValue &mapentry = map_[name];
|
||||||
if ((mapentry.type != SimpleDynamicTag::UNINITIALIZED) && (mapentry.type != parsedvalue.type)) {
|
|
||||||
return util::ss("animation '", name, "' must be a ", mapentry.type_name());
|
|
||||||
}
|
|
||||||
mapentry.copy_value(parsedvalue);
|
mapentry.copy_value(parsedvalue);
|
||||||
if (setpersist) mapentry.persistent = true;
|
mapentry.persistent = persistent;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eng::string AnimState::merge(const AnimState &previous, const AnimState &update) {
|
||||||
|
// Copy everything over from the previous entry.
|
||||||
|
map_ = previous.map_;
|
||||||
|
|
||||||
|
for (const auto &pair : update.map_) {
|
||||||
|
const eng::string &name = pair.first;
|
||||||
|
AnimValue &dst = map_[name];
|
||||||
|
const AnimValue &src = pair.second;
|
||||||
|
|
||||||
|
// Handle autocalculation rules.
|
||||||
|
if (src.type == SimpleDynamicTag::AUTO) {
|
||||||
|
if (name == "facing") {
|
||||||
|
if (!dst.persistent || dst.type != SimpleDynamicTag::NUMBER) {
|
||||||
|
return "Cannot auto-calculate facing because facing has not been specified as a persistent number";
|
||||||
|
}
|
||||||
|
const auto xyz_previous = previous.map_.find("xyz");
|
||||||
|
const auto xyz_update = update.map_.find("xyz");
|
||||||
|
if ((xyz_previous == previous.map_.end()) ||
|
||||||
|
(xyz_update == update.map_.end()) ||
|
||||||
|
(xyz_previous->second.type != SimpleDynamicTag::VECTOR) ||
|
||||||
|
(xyz_update->second.type != SimpleDynamicTag::VECTOR)) {
|
||||||
|
return "Cannot auto-calculate facing because before/after xyz coordinates are not present";
|
||||||
|
}
|
||||||
|
double dx = xyz_update->second.x - xyz_previous->second.x;
|
||||||
|
double dy = xyz_update->second.y - xyz_previous->second.y;
|
||||||
|
// If dx and dy are both zero, leave the facing unmodified.
|
||||||
|
if ((dx != 0.0) || (dy != 0.0)) {
|
||||||
|
double facing = atan2(dy, dx) * 180.0 / M_PI;
|
||||||
|
dst.set_number(facing);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return util::ss("No rule to automatically calculate ", name);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst.persistent && (src.type != dst.type)) {
|
||||||
|
return util::ss("Wrong data type for ", name, ", should be ", dst.type_name());
|
||||||
|
}
|
||||||
|
dst.copy_value(src);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AnimState::to_lua(LuaCoreStack &LS0, LuaSlot tab, bool persistent) {
|
void AnimState::to_lua(LuaCoreStack &LS0, LuaSlot tab, bool persistent) {
|
||||||
LuaVar name, val;
|
LuaVar name, val;
|
||||||
LuaExtStack LS(LS0.state(), name, val);
|
LuaExtStack LS(LS0.state(), name, val);
|
||||||
@@ -269,6 +326,7 @@ void AnimState::to_lua(LuaCoreStack &LS0, LuaSlot tab, bool persistent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The syntax used by this parser is not general enough to represent all
|
// The syntax used by this parser is not general enough to represent all
|
||||||
// possible strings. That's OK, though, since it's just for unit testing.
|
// possible strings. That's OK, though, since it's just for unit testing.
|
||||||
void AnimState::parse(std::string_view config) {
|
void AnimState::parse(std::string_view config) {
|
||||||
|
|||||||
@@ -185,12 +185,35 @@ public:
|
|||||||
//
|
//
|
||||||
eng::string add_defaults(const AnimState *other);
|
eng::string add_defaults(const AnimState *other);
|
||||||
|
|
||||||
// Apply a configuration from a lua table.
|
// Parse an animstate from a Lua Table.
|
||||||
//
|
//
|
||||||
// If a key already exists in the state, then type type from the table
|
// Table keys must be valid lua identifiers. Table values may be string,
|
||||||
// must match the type from the existing state.
|
// floats, bool, or coordinates. Persistent flags are all set the same,
|
||||||
|
// from the persistent parameter. Returns empty string on success, or an
|
||||||
|
// error message on failure.
|
||||||
//
|
//
|
||||||
eng::string apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist);
|
// If 'allowauto' is true, then the lua table may contain a key-value
|
||||||
|
// pair of the form (key, math.auto). These keys will be stored in the
|
||||||
|
// AnimState map with mapentry.type == SimpleDynamicTag::AUTO.
|
||||||
|
// This is done to express an intent that the value should be
|
||||||
|
// automatically computer later.
|
||||||
|
//
|
||||||
|
eng::string from_lua(LuaCoreStack &LS0, LuaSlot tab, bool persistent, bool allowauto);
|
||||||
|
|
||||||
|
// Generate a merged animstate using a previous state and an update.
|
||||||
|
//
|
||||||
|
// Keys from both previous and update are combined to create this AnimState.
|
||||||
|
// Values from 'update' override values from 'previous'. Persistent flags
|
||||||
|
// are taken from 'previous'. If a key exists in both previous and update,
|
||||||
|
// and the key is persistent in 'previous', then the types must match,
|
||||||
|
// otherwise an error is generated. Returns empty string on success or
|
||||||
|
// an error message on failure.
|
||||||
|
//
|
||||||
|
// If a key in the 'update' map has type AUTO, then we will attempt to find
|
||||||
|
// a rule to compute that value automatically. Failure to find a rule
|
||||||
|
// results in an error.
|
||||||
|
//
|
||||||
|
eng::string merge(const AnimState &previous, const AnimState &update);
|
||||||
|
|
||||||
// Convert an animstate to a lua table.
|
// Convert an animstate to a lua table.
|
||||||
//
|
//
|
||||||
@@ -294,6 +317,17 @@ public:
|
|||||||
//
|
//
|
||||||
util::SharedStdString get_encoded_queue() const { return encqueue_; }
|
util::SharedStdString get_encoded_queue() const { return encqueue_; }
|
||||||
|
|
||||||
|
// Get a serialized representation of a blank queue.
|
||||||
|
//
|
||||||
|
// Since an animqueue must have at least one step, the blank queue
|
||||||
|
// contains a single default step.
|
||||||
|
//
|
||||||
|
static util::SharedStdString get_encoded_blank_queue() { return blankqueue_; }
|
||||||
|
|
||||||
|
// Initialize the animqueue module.
|
||||||
|
//
|
||||||
|
static void initialize_module();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct QueueRange {
|
struct QueueRange {
|
||||||
int size;
|
int size;
|
||||||
@@ -343,6 +377,10 @@ private:
|
|||||||
// can't have the graphics engine affecting the behavior of the engine heap.
|
// can't have the graphics engine affecting the behavior of the engine heap.
|
||||||
//
|
//
|
||||||
util::SharedStdString encqueue_;
|
util::SharedStdString encqueue_;
|
||||||
|
|
||||||
|
// The blank animation queue.
|
||||||
|
//
|
||||||
|
static util::SharedStdString blankqueue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ANIMQUEUE_HPP
|
#endif // ANIMQUEUE_HPP
|
||||||
|
|||||||
@@ -426,9 +426,8 @@ void DrivenEngine::drv_get_animation_queues(uint32_t count, const int64_t *ids,
|
|||||||
anim_queues_.resize(count);
|
anim_queues_.resize(count);
|
||||||
|
|
||||||
if (visible_world_ == nullptr) {
|
if (visible_world_ == nullptr) {
|
||||||
util::SharedStdString empty = std::make_shared<std::string>("");
|
|
||||||
for (int i = 0; i < int(count); i++) {
|
for (int i = 0; i < int(count); i++) {
|
||||||
anim_queues_[i] = empty;
|
anim_queues_[i] = AnimQueue::get_encoded_blank_queue();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
visible_world_->get_encoded_animation_queues(count, ids, anim_queues_);
|
visible_world_->get_encoded_animation_queues(count, ids, anim_queues_);
|
||||||
|
|||||||
@@ -572,7 +572,6 @@ eng::string LuaKeywordParser::final_check() {
|
|||||||
if (not_table_) {
|
if (not_table_) {
|
||||||
return "expected a keyword table";
|
return "expected a keyword table";
|
||||||
}
|
}
|
||||||
if (lua_nkeys(L_, slot_) != int(parsed_.size())) {
|
|
||||||
lua_pushnil(L_);
|
lua_pushnil(L_);
|
||||||
while (lua_next(L_, slot_) != 0) {
|
while (lua_next(L_, slot_) != 0) {
|
||||||
lua_pop(L_, 1); // Don't need the value.
|
lua_pop(L_, 1); // Don't need the value.
|
||||||
@@ -586,8 +585,6 @@ eng::string LuaKeywordParser::final_check() {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(false && "should never get here in check_unparsed_keywords");
|
|
||||||
}
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -765,6 +765,7 @@ LuaNumberConstant(math_pi, M_PI, "");
|
|||||||
LuaNumberConstant(math_huge, HUGE_VAL, "");
|
LuaNumberConstant(math_huge, HUGE_VAL, "");
|
||||||
LuaNumberConstant(math_nan, NAN, "");
|
LuaNumberConstant(math_nan, NAN, "");
|
||||||
LuaNumberConstant(math_maxint, LuaCoreStack::MAXINT, "");
|
LuaNumberConstant(math_maxint, LuaCoreStack::MAXINT, "");
|
||||||
|
LuaTokenConstant(math_auto, "auto", "A value used to request that a value should be automatically computed");
|
||||||
|
|
||||||
// math.random and math.randomseed are in world-accessor.cpp, because
|
// math.random and math.randomseed are in world-accessor.cpp, because
|
||||||
// generating random numbers must manipulate global state which is
|
// generating random numbers must manipulate global state which is
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ LuaDefine(tangible_animinit, "tan,config",
|
|||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||||
AnimState state;
|
AnimState state;
|
||||||
eng::string error = state.apply_lua(LS, config, true);
|
eng::string error = state.from_lua(LS, config, true, false);
|
||||||
if (!error.empty()) {
|
if (!error.empty()) {
|
||||||
luaL_error(L, "%s", error.c_str());
|
luaL_error(L, "%s", error.c_str());
|
||||||
}
|
}
|
||||||
@@ -185,18 +185,25 @@ LuaDefine(tangible_animate, "tan,options,config",
|
|||||||
if (kp.parse(option, "replace")) {
|
if (kp.parse(option, "replace")) {
|
||||||
replace = LS.ckboolean(option);
|
replace = LS.ckboolean(option);
|
||||||
}
|
}
|
||||||
|
kp.final_check_throw();
|
||||||
}
|
}
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||||
AnimState state = tan->anim_queue_.get_final_persistent();
|
AnimState previous = tan->anim_queue_.get_final_persistent();
|
||||||
eng::string error = state.apply_lua(LS, steptab, false);
|
AnimState update;
|
||||||
|
eng::string error = update.from_lua(LS, steptab, false, true);
|
||||||
|
if (!error.empty()) {
|
||||||
|
luaL_error(L, "%s", error.c_str());
|
||||||
|
}
|
||||||
|
AnimState merge;
|
||||||
|
error = merge.merge(previous, update);
|
||||||
if (!error.empty()) {
|
if (!error.empty()) {
|
||||||
luaL_error(L, "%s", error.c_str());
|
luaL_error(L, "%s", error.c_str());
|
||||||
}
|
}
|
||||||
if (replace) {
|
if (replace) {
|
||||||
tan->anim_queue_.replace(state);
|
tan->anim_queue_.replace(merge);
|
||||||
} else {
|
} else {
|
||||||
tan->anim_queue_.add(state);
|
tan->anim_queue_.add(merge);
|
||||||
}
|
}
|
||||||
tan->update_plane_item();
|
tan->update_plane_item();
|
||||||
return LS.result();
|
return LS.result();
|
||||||
@@ -289,7 +296,7 @@ LuaDefine(tangible_build, "config",
|
|||||||
|
|
||||||
// Calculate the initial animation state.
|
// Calculate the initial animation state.
|
||||||
AnimState state;
|
AnimState state;
|
||||||
err = state.apply_lua(LS, animstate, true);
|
err = state.from_lua(LS, animstate, true, false);
|
||||||
if (err != "") {
|
if (err != "") {
|
||||||
luaL_error(L, "%s", err.c_str());
|
luaL_error(L, "%s", err.c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,12 +234,11 @@ void World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
void World::get_encoded_animation_queues(uint32_t count, const int64_t *ids, util::SharedStdStringVec &into) {
|
void World::get_encoded_animation_queues(uint32_t count, const int64_t *ids, util::SharedStdStringVec &into) {
|
||||||
util::SharedStdString empty = std::make_shared<std::string>("");
|
|
||||||
into.resize(count);
|
into.resize(count);
|
||||||
for (int i = 0; i < int(count); i++) {
|
for (int i = 0; i < int(count); i++) {
|
||||||
Tangible *tan = tangible_get(ids[i]);
|
Tangible *tan = tangible_get(ids[i]);
|
||||||
if (tan == nullptr) {
|
if (tan == nullptr) {
|
||||||
into[i] = empty;
|
into[i] = AnimQueue::get_encoded_blank_queue();
|
||||||
} else {
|
} else {
|
||||||
into[i] = tan->anim_queue_.get_encoded_queue();
|
into[i] = tan->anim_queue_.get_encoded_queue();
|
||||||
}
|
}
|
||||||
@@ -1191,6 +1190,7 @@ void World::rollback() {
|
|||||||
//
|
//
|
||||||
void engine_initialization() {
|
void engine_initialization() {
|
||||||
SourceDB::register_lua_builtins();
|
SourceDB::register_lua_builtins();
|
||||||
|
AnimQueue::initialize_module();
|
||||||
}
|
}
|
||||||
|
|
||||||
static DrivenEngineInitializerReg eireg(engine_initialization);
|
static DrivenEngineInitializerReg eireg(engine_initialization);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
enum class SimpleDynamicTag {
|
enum class SimpleDynamicTag {
|
||||||
UNINITIALIZED,
|
UNINITIALIZED,
|
||||||
|
AUTO,
|
||||||
STRING,
|
STRING,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
BOOLEAN,
|
BOOLEAN,
|
||||||
@@ -70,6 +71,10 @@ struct SimpleDynamic {
|
|||||||
type=SimpleDynamicTag::UNINITIALIZED; s.clear(); x=y=z=0;
|
type=SimpleDynamicTag::UNINITIALIZED; s.clear(); x=y=z=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_auto() {
|
||||||
|
type=SimpleDynamicTag::AUTO; s.clear(); x=y=z=0;
|
||||||
|
}
|
||||||
|
|
||||||
void set_string(std::string_view is) {
|
void set_string(std::string_view is) {
|
||||||
type=SimpleDynamicTag::STRING; s=is; x=y=z=0;
|
type=SimpleDynamicTag::STRING; s=is; x=y=z=0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ function login.initialize(actor, place)
|
|||||||
dprint("login.initialize:", actor)
|
dprint("login.initialize:", actor)
|
||||||
local x = math.random(1, 100)
|
local x = math.random(1, 100)
|
||||||
local y = math.random(1, 100)
|
local y = math.random(1, 100)
|
||||||
tangible.animate(actor, nil, {action="warpto", plane="earth", xyz={x, y, 90}})
|
tangible.animate(actor, nil, {bp="tangiblecharacter", action="warpto", plane="earth", xyz={x, y, 90}})
|
||||||
end
|
end
|
||||||
|
|
||||||
function login.interface(actor, place)
|
function login.interface(actor, place)
|
||||||
|
|||||||
Reference in New Issue
Block a user