Files
integration/luprex/core/cpp/animqueue.cpp

377 lines
11 KiB
C++
Raw Normal View History

2021-01-12 14:14:38 -05:00
#include <limits>
2021-01-23 14:44:06 -05:00
#include "luastack.hpp"
#include "animqueue.hpp"
2021-01-12 14:14:38 -05:00
AnimStep::AnimStep() {}
AnimStep::~AnimStep() {}
2021-01-17 16:23:10 -05:00
AnimQueue::AnimQueue() {
size_limit_ = 10; // Default size limit.
clear_steps();
}
void AnimQueue::clear_steps() {
steps_.clear();
2021-01-12 14:14:38 -05:00
steps_.emplace_back();
AnimStep &init = steps_.back();
init.id_ = 0;
init.bits_ = AnimStep::HAS_EVERYTHING;
2021-01-12 14:14:38 -05:00
init.facing_ = 0;
init.xyz_ = util::XYZ(0,0,0);
init.graphic_ = "nothing";
init.plane_ = "nowhere";
}
2021-01-17 16:23:10 -05:00
void AnimQueue::set_size_limit(int n) {
assert(n >= 2);
size_limit_ = n;
}
2021-02-25 16:32:48 -05:00
void AnimQueue::add(int64_t id, lua_State *L, int idx) {
LuaSpecial tab(idx);
LuaVar value;
LuaStack LS(L, value);
if (!LS.istable(tab)) {
luaL_error(L, "animation spec must be a table");
}
AnimStep step = steps_.back();
step.id_ = id;
step.bits_ = 0;
step.action_ = "";
LS.rawget(value, tab, "action");
if (!LS.isstring(value)) {
luaL_error(L, "animation action is not optional and must be a string");
}
step.action_ = LS.ckstring(value);
LS.rawget(value, tab, "facing");
if (LS.isnumber(value)) {
step.facing_ = LS.cknumber(value);
step.bits_ |= AnimStep::HAS_FACING;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation facing must be a number");
}
LS.rawget(value, tab, "x");
if (LS.isnumber(value)) {
step.xyz_.x = LS.cknumber(value);
step.bits_ |= AnimStep::HAS_XYZ;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation X coordinate must be a number");
}
LS.rawget(value, tab, "y");
if (LS.isnumber(value)) {
step.xyz_.y = LS.cknumber(value);
step.bits_ |= AnimStep::HAS_XYZ;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation Y coordinate must be a number");
}
LS.rawget(value, tab, "z");
if (LS.isnumber(value)) {
step.xyz_.z = LS.cknumber(value);
step.bits_ |= AnimStep::HAS_XYZ;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation Z coordinate must be a number");
}
LS.rawget(value, tab, "dx");
if (LS.isnumber(value)) {
step.xyz_.x += LS.cknumber(value);
step.bits_ |= AnimStep::HAS_XYZ;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation DX offset must be a number");
}
LS.rawget(value, tab, "dy");
if (LS.isnumber(value)) {
step.xyz_.y += LS.cknumber(value);
step.bits_ |= AnimStep::HAS_XYZ;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation DY offset must be a number");
}
LS.rawget(value, tab, "dz");
if (LS.isnumber(value)) {
step.xyz_.z += LS.cknumber(value);
step.bits_ |= AnimStep::HAS_XYZ;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation DZ offset must be a number");
}
LS.rawget(value, tab, "graphic");
if (LS.isstring(value)) {
step.graphic_ = LS.ckstring(value);
step.bits_ |= AnimStep::HAS_GRAPHIC;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation graphic must be a string");
}
LS.rawget(value, tab, "plane");
if (LS.isstring(value)) {
step.plane_ = LS.ckstring(value);
step.bits_ |= AnimStep::HAS_PLANE;
} else if (!LS.isnil(value)) {
luaL_error(L, "animation plane must be a string");
}
steps_.push_back(step);
while (int(steps_.size()) > size_limit_) {
steps_.pop_front();
}
AnimStep &init = steps_.front();
init.id_ = 0;
init.action_ = "";
init.bits_ = AnimStep::HAS_EVERYTHING;
}
2021-01-12 14:14:38 -05:00
void AnimQueue::add(int64_t id, const std::string &action) {
2021-02-25 16:32:48 -05:00
AnimStep step = steps_.back();
step.id_ = id;
step.action_ = action;
step.bits_ = 0;
2021-01-12 14:14:38 -05:00
2021-02-25 16:32:48 -05:00
steps_.push_back(step);
2021-01-12 14:14:38 -05:00
while (int(steps_.size()) > size_limit_) {
steps_.pop_front();
}
AnimStep &init = steps_.front();
init.id_ = 0;
init.action_ = "";
2021-01-16 01:24:33 -05:00
init.bits_ = AnimStep::HAS_EVERYTHING;
2021-01-12 14:14:38 -05:00
}
void AnimQueue::set_facing(float f) {
AnimStep &last = steps_.back();
last.bits_ |= AnimStep::HAS_FACING;
last.facing_ = f;
}
void AnimQueue::set_xyz(util::XYZ xyz) {
AnimStep &last = steps_.back();
last.bits_ |= AnimStep::HAS_XYZ;
last.xyz_ = xyz;
}
void AnimQueue::set_graphic(const std::string &g) {
AnimStep &last = steps_.back();
last.bits_ |= AnimStep::HAS_GRAPHIC;
last.graphic_ = g;
}
void AnimQueue::set_plane(const std::string &p) {
AnimStep &last = steps_.back();
last.bits_ |= AnimStep::HAS_PLANE;
last.plane_ = p;
}
2021-01-16 01:24:33 -05:00
bool AnimQueue::valid() {
// Animqueue must have at least one step.
if (steps_.empty()) {
return false;
}
// First action should be blank.
if (steps_[0].action_ != "") {
return false;
}
// First step should have all bits.
if (steps_[0].bits_ != AnimStep::HAS_EVERYTHING) {
return false;
}
// Any unset bit should correspond to a value copied from the previous step.
for (size_t i = 1; i < steps_.size(); i++) {
const AnimStep &prev = steps_[i - 1];
const AnimStep &curr = steps_[i];
if (!curr.has_facing() && (curr.facing_ != prev.facing_)) {
return false;
}
if (!curr.has_xyz() && (curr.xyz_ != prev.xyz_)) {
return false;
}
if (!curr.has_graphic() && (curr.graphic_ != prev.graphic_)) {
return false;
}
if (!curr.has_plane() && (curr.plane_ != prev.plane_)) {
return false;
}
}
return true;
}
void AnimQueue::serialize(StreamBuffer *sb) {
assert(valid()); // can't serialize an invalid animqueue.
sb->write_int32(size_limit_);
sb->write_size(steps_.size());
for (const AnimStep &step : steps_) {
sb->write_int64(step.id_);
sb->write_int16(step.bits_);
sb->write_string(step.action_);
if (step.has_facing()) {
sb->write_float(step.facing_);
}
if (step.has_xyz()) {
sb->write_float(step.xyz_.x);
sb->write_float(step.xyz_.y);
sb->write_float(step.xyz_.z);
}
if (step.has_graphic()) {
sb->write_string(step.graphic_);
}
if (step.has_plane()) {
sb->write_string(step.plane_);
}
}
}
void AnimQueue::deserialize(StreamBuffer *sb) {
size_limit_ = sb->read_int32();
size_t nsteps = sb->read_size();
steps_.resize(nsteps);
for (size_t i = 0; i < nsteps; i++) {
AnimStep &step = steps_[i];
if (i > 0) step = steps_[i - 1];
step.id_ = sb->read_int64();
step.bits_ = sb->read_int16();
step.action_ = sb->read_string();
if (step.has_facing()) {
step.facing_ = sb->read_float();
}
if (step.has_xyz()) {
step.xyz_.x = sb->read_float();
step.xyz_.y = sb->read_float();
step.xyz_.z = sb->read_float();
}
if (step.has_graphic()) {
step.graphic_ = sb->read_string();
}
if (step.has_plane()) {
step.plane_ = sb->read_string();
}
}
}
2021-01-22 17:35:23 -05:00
const std::string &AnimQueue::get_graphic() const {
const AnimStep &last = steps_.back();
return last.graphic_;
}
2021-01-16 01:24:33 -05:00
const std::string &AnimQueue::get_plane() const {
const AnimStep &last = steps_.back();
return last.plane_;
}
const util::XYZ &AnimQueue::get_xyz() const {
const AnimStep &last = steps_.back();
return last.xyz_;
}
LuaDefine(unittests_animqueue, "c") {
// Check initial state.
2021-01-17 16:23:10 -05:00
AnimQueue aq;
StreamBuffer sb;
AnimQueue aqds;
2021-01-17 16:23:10 -05:00
aq.set_size_limit(3);
LuaAssert(L, aq.valid());
2021-01-16 01:24:33 -05:00
LuaAssert(L, aq.size() == 1);
const AnimStep *st = &aq.nth(0);
LuaAssert(L, st->id() == 0);
LuaAssert(L, st->action() == "");
LuaAssert(L, st->bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, st->facing() == 0.0);
LuaAssert(L, st->xyz() == util::XYZ(0,0,0));
LuaAssert(L, st->graphic() == "nothing");
LuaAssert(L, st->plane() == "nowhere");
// Add a step.
aq.add(12345, "walk");
LuaAssert(L, aq.valid());
2021-01-16 01:24:33 -05:00
LuaAssert(L, aq.size() == 2);
st = &aq.nth(1);
LuaAssert(L, st->id() == 12345);
LuaAssert(L, st->action() == "walk");
LuaAssert(L, st->bits() == 0);
LuaAssert(L, st->facing() == 0.0);
LuaAssert(L, st->xyz() == util::XYZ(0,0,0));
LuaAssert(L, st->graphic() == "nothing");
LuaAssert(L, st->plane() == "nowhere");
// Test the setters.
aq.set_facing(180);
LuaAssert(L, st->facing() == 180);
LuaAssert(L, st->bits() == AnimStep::HAS_FACING);
aq.set_xyz(util::XYZ(3,4,5));
LuaAssert(L, st->xyz() == util::XYZ(3, 4, 5));
LuaAssert(L, st->bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ));
aq.set_plane("somewhere");
LuaAssert(L, st->plane() == "somewhere");
LuaAssert(L, st->bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ | AnimStep::HAS_PLANE));
aq.set_graphic("something");
LuaAssert(L, st->graphic() == "something");
LuaAssert(L, st->bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ | AnimStep::HAS_PLANE | AnimStep::HAS_GRAPHIC));
LuaAssert(L, aq.valid());
2021-01-16 01:24:33 -05:00
// Exceed the length limit, dropping first element.
aq.add(12346, "walk");
aq.add(12347, "walk");
LuaAssert(L, aq.size() == 3);
LuaAssert(L, aq.nth(0).id() == 0);
LuaAssert(L, aq.nth(0).action() == "");
LuaAssert(L, aq.nth(0).bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, aq.nth(1).id() == 12346);
LuaAssert(L, aq.nth(2).id() == 12347);
LuaAssert(L, aq.valid());
// Test serialization and deserialization.
aq.set_size_limit(5);
aq.clear_steps();
aq.add(12345, "walk");
aq.set_xyz(util::XYZ(3,4,5));
aq.add(12346, "setgraphic");
aq.set_graphic("banana");
aq.add(12347, "setfacing");
aq.set_facing(301.0);
aq.serialize(&sb);
aqds.deserialize(&sb);
LuaAssert(L, aqds.size_limit() == 5);
LuaAssert(L, aqds.size() == 4);
LuaAssert(L, aqds.nth(0).id() == 0);
LuaAssert(L, aqds.nth(0).bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, aqds.nth(0).action() == "");
LuaAssert(L, aqds.nth(0).facing() == 0.0);
LuaAssert(L, aqds.nth(0).xyz() == util::XYZ(0,0,0));
LuaAssert(L, aqds.nth(0).graphic() == "nothing");
LuaAssert(L, aqds.nth(0).plane() == "nowhere");
LuaAssert(L, aqds.nth(1).id() == 12345);
LuaAssert(L, aqds.nth(1).bits() == AnimStep::HAS_XYZ);
LuaAssert(L, aqds.nth(1).action() == "walk");
LuaAssert(L, aqds.nth(1).facing() == 0.0);
LuaAssert(L, aqds.nth(1).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(1).graphic() == "nothing");
LuaAssert(L, aqds.nth(1).plane() == "nowhere");
LuaAssert(L, aqds.nth(2).id() == 12346);
LuaAssert(L, aqds.nth(2).bits() == AnimStep::HAS_GRAPHIC);
LuaAssert(L, aqds.nth(2).action() == "setgraphic");
LuaAssert(L, aqds.nth(2).facing() == 0.0);
LuaAssert(L, aqds.nth(2).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(2).graphic() == "banana");
LuaAssert(L, aqds.nth(2).plane() == "nowhere");
LuaAssert(L, aqds.nth(3).id() == 12347);
LuaAssert(L, aqds.nth(3).bits() == AnimStep::HAS_FACING);
LuaAssert(L, aqds.nth(3).action() == "setfacing");
LuaAssert(L, aqds.nth(3).facing() == 301.0);
LuaAssert(L, aqds.nth(3).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(3).graphic() == "banana");
LuaAssert(L, aqds.nth(3).plane() == "nowhere");
2021-01-16 01:24:33 -05:00
return 0;
}