From 98d261ce378302445f384b0da9a0781af08f6c63 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Sun, 28 Mar 2021 15:12:09 -0400 Subject: [PATCH] Overhaul of animstep, implement tangible.build --- luprex/core/cpp/animqueue.cpp | 355 +++++++++++++++++++--------------- luprex/core/cpp/animqueue.hpp | 91 +++++---- luprex/core/cpp/luastack.cpp | 2 +- luprex/core/cpp/world.cpp | 59 +++++- 4 files changed, 316 insertions(+), 191 deletions(-) diff --git a/luprex/core/cpp/animqueue.cpp b/luprex/core/cpp/animqueue.cpp index 7a4d4ce3..7168b70b 100644 --- a/luprex/core/cpp/animqueue.cpp +++ b/luprex/core/cpp/animqueue.cpp @@ -2,32 +2,67 @@ #include "luastack.hpp" #include "animqueue.hpp" -AnimStep::AnimStep() {} +AnimStep::AnimStep() { + clear(); +} + AnimStep::~AnimStep() {} +void AnimStep::clear() { + id_ = 0; + bits_ = 0; + action_ = ""; + facing_ = 0; + xyz_ = util::XYZ(0,0,0); + graphic_ = ""; + plane_ = ""; +} + +void AnimStep::set_action(const std::string &act) { + action_ = act; +} + +void AnimStep::set_facing(float f) { + bits_ |= HAS_FACING; + facing_ = f; +} + +void AnimStep::set_x(float f) { + bits_ |= HAS_X; + xyz_.x = f; +} + +void AnimStep::set_y(float f) { + bits_ |= HAS_Y; + xyz_.y = f; +} + +void AnimStep::set_z(float f) { + bits_ |= HAS_Z; + xyz_.z = f; +} + +void AnimStep::set_xyz(const util::XYZ &xyz) { + bits_ |= (HAS_X | HAS_Y | HAS_Z); + xyz_ = xyz; +} + +void AnimStep::set_graphic(const std::string &g) { + bits_ |= HAS_GRAPHIC; + graphic_ = g; +} + +void AnimStep::set_plane(const std::string &p) { + bits_ |= HAS_PLANE; + plane_ = p; +} + AnimQueue::AnimQueue() { size_limit_ = 10; // Default size limit. clear_steps(); } -void AnimQueue::clear_steps() { - steps_.clear(); - steps_.emplace_back(); - AnimStep &init = steps_.back(); - init.id_ = 0; - init.bits_ = AnimStep::HAS_EVERYTHING; - init.facing_ = 0; - init.xyz_ = util::XYZ(0,0,0); - init.graphic_ = "nothing"; - init.plane_ = "nowhere"; -} - -void AnimQueue::set_size_limit(int n) { - assert(n >= 2); - size_limit_ = n; -} - -void AnimQueue::add(int64_t id, lua_State *L, int idx) { +void AnimStep::from_lua(lua_State *L, int idx) { LuaSpecial tab(idx); LuaVar value; LuaStack LS(L, value); @@ -35,91 +70,123 @@ void AnimQueue::add(int64_t id, lua_State *L, int idx) { 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"); + if (LS.isstring(value)) { + action_ = LS.ckstring(value); + } else if (!LS.isnil(value)) { + luaL_error(L, "animation action 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; + facing_ = LS.cknumber(value); + bits_ |= 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; + xyz_.x = LS.cknumber(value); + bits_ |= HAS_X; } 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; + xyz_.y = LS.cknumber(value); + bits_ |= HAS_Y; } 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; + xyz_.z = LS.cknumber(value); + bits_ |= HAS_Z; } 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; + graphic_ = LS.ckstring(value); + bits_ |= 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; + plane_ = LS.ckstring(value); + bits_ |= 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_) { +void AnimStep::echo(const AnimStep &prev) { + if (!has_facing()) { + facing_ = prev.facing_; + } + if (!has_x()) { + xyz_.x = prev.xyz_.x; + } + if (!has_y()) { + xyz_.y = prev.xyz_.y; + } + if (!has_z()) { + xyz_.z = prev.xyz_.z; + } + if (!has_graphic()) { + graphic_ = prev.graphic_; + } + if (!has_plane()) { + plane_ = prev.plane_; + } +} + +bool AnimStep::echoes(const AnimStep &prev) const { + if (!has_facing() && (facing_ != prev.facing_)) { + return false; + } + if (!has_x() && (xyz_.x != prev.xyz_.x)) { + return false; + } + if (!has_y() && (xyz_.y != prev.xyz_.y)) { + return false; + } + if (!has_z() && (xyz_.z != prev.xyz_.z)) { + return false; + } + if (!has_graphic() && (graphic_ != prev.graphic_)) { + return false; + } + if (!has_plane() && (plane_ != prev.plane_)) { + return false; + } + return true; +} + +void AnimQueue::clear_steps() { + steps_.clear(); + steps_.emplace_back(); + AnimStep &init = steps_.back(); + init.bits_ = AnimStep::HAS_EVERYTHING; + init.action_ = ""; + init.graphic_ = ""; + init.plane_ = ""; +} + +void AnimQueue::set_size_limit(int n) { + assert(n >= 2); + size_limit_ = n; +} + +void AnimQueue::keep_only(int n) { + if (n < 1) n = 1; + while (int(steps_.size()) > n) { steps_.pop_front(); } AnimStep &init = steps_.front(); @@ -128,44 +195,12 @@ void AnimQueue::add(int64_t id, lua_State *L, int idx) { init.bits_ = AnimStep::HAS_EVERYTHING; } -void AnimQueue::add(int64_t id, const std::string &action) { - AnimStep step = steps_.back(); - step.id_ = id; - step.action_ = action; - step.bits_ = 0; - - 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; -} - -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; +void AnimQueue::add(int64_t id, const AnimStep &step) { + AnimStep copy = step; + copy.echo(steps_.back()); + copy.id_ = id; + steps_.push_back(copy); + keep_only(size_limit_); } bool AnimQueue::valid() { @@ -185,18 +220,7 @@ bool AnimQueue::valid() { 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; - } + if (!curr.echoes(prev)) return false; } return true; } @@ -212,9 +236,13 @@ void AnimQueue::serialize(StreamBuffer *sb) { if (step.has_facing()) { sb->write_float(step.facing_); } - if (step.has_xyz()) { + if (step.has_x()) { sb->write_float(step.xyz_.x); + } + if (step.has_y()) { sb->write_float(step.xyz_.y); + } + if (step.has_z()) { sb->write_float(step.xyz_.z); } if (step.has_graphic()) { @@ -239,9 +267,13 @@ void AnimQueue::deserialize(StreamBuffer *sb) { if (step.has_facing()) { step.facing_ = sb->read_float(); } - if (step.has_xyz()) { + if (step.has_x()) { step.xyz_.x = sb->read_float(); + } + if (step.has_y()) { step.xyz_.y = sb->read_float(); + } + if (step.has_z()) { step.xyz_.z = sb->read_float(); } if (step.has_graphic()) { @@ -259,6 +291,7 @@ const AnimStep &AnimQueue::back() const { LuaDefine(unittests_animqueue, "c") { // Check initial state. + AnimStep stp; AnimQueue aq; StreamBuffer sb; AnimQueue aqds; @@ -272,11 +305,34 @@ LuaDefine(unittests_animqueue, "c") { 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"); + LuaAssert(L, st->graphic() == ""); + LuaAssert(L, st->plane() == ""); + + // Test the step setters. + stp.clear(); + stp.set_facing(180); + LuaAssert(L, stp.facing() == 180); + LuaAssert(L, stp.bits() == AnimStep::HAS_FACING); + stp.set_x(3); + LuaAssert(L, stp.xyz() == util::XYZ(3, 0, 0)); + LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_X)); + stp.set_y(4); + LuaAssert(L, stp.xyz() == util::XYZ(3, 4, 0)); + LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_X | AnimStep::HAS_Y)); + stp.set_z(5); + LuaAssert(L, stp.xyz() == util::XYZ(3, 4, 5)); + LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_X | AnimStep::HAS_Y | AnimStep::HAS_Z)); + stp.set_plane("somewhere"); + LuaAssert(L, stp.plane() == "somewhere"); + LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ | AnimStep::HAS_PLANE)); + stp.set_graphic("something"); + LuaAssert(L, stp.graphic() == "something"); + LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ | AnimStep::HAS_PLANE | AnimStep::HAS_GRAPHIC)); // Add a step. - aq.add(12345, "walk"); + stp.clear(); + stp.set_action("walk"); + aq.add(12345, stp); LuaAssert(L, aq.valid()); LuaAssert(L, aq.size() == 2); st = &aq.nth(1); @@ -285,27 +341,14 @@ LuaDefine(unittests_animqueue, "c") { 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"); + LuaAssert(L, st->graphic() == ""); + LuaAssert(L, st->plane() == ""); - // 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()); - // Exceed the length limit, dropping first element. - aq.add(12346, "walk"); - aq.add(12347, "walk"); + stp.clear(); + stp.set_action("walk"); + aq.add(12346, stp); + aq.add(12347, stp); LuaAssert(L, aq.size() == 3); LuaAssert(L, aq.nth(0).id() == 0); LuaAssert(L, aq.nth(0).action() == ""); @@ -317,12 +360,22 @@ LuaDefine(unittests_animqueue, "c") { // 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); + + stp.clear(); + stp.set_action("walk"); + stp.set_xyz(util::XYZ(3,4,5)); + aq.add(12345, stp); + + stp.clear(); + stp.set_action("setgraphic"); + stp.set_graphic("banana"); + aq.add(12346, stp); + + stp.clear(); + stp.set_action("setfacing"); + stp.set_facing(301.0); + aq.add(12347, stp); + aq.serialize(&sb); aqds.deserialize(&sb); @@ -334,16 +387,16 @@ LuaDefine(unittests_animqueue, "c") { 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(0).graphic() == ""); + LuaAssert(L, aqds.nth(0).plane() == ""); 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(1).graphic() == ""); + LuaAssert(L, aqds.nth(1).plane() == ""); LuaAssert(L, aqds.nth(2).id() == 12346); LuaAssert(L, aqds.nth(2).bits() == AnimStep::HAS_GRAPHIC); @@ -351,7 +404,7 @@ LuaDefine(unittests_animqueue, "c") { 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(2).plane() == ""); LuaAssert(L, aqds.nth(3).id() == 12347); LuaAssert(L, aqds.nth(3).bits() == AnimStep::HAS_FACING); @@ -359,7 +412,7 @@ LuaDefine(unittests_animqueue, "c") { 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"); + LuaAssert(L, aqds.nth(3).plane() == ""); return 0; } diff --git a/luprex/core/cpp/animqueue.hpp b/luprex/core/cpp/animqueue.hpp index 1ce1ddb4..9ddcafec 100644 --- a/luprex/core/cpp/animqueue.hpp +++ b/luprex/core/cpp/animqueue.hpp @@ -38,10 +38,13 @@ class AnimStep { public: enum { HAS_FACING = 1, - HAS_XYZ = 2, - HAS_GRAPHIC = 4, - HAS_PLANE = 8, - HAS_EVERYTHING = 15, + HAS_X = 2, + HAS_Y = 4, + HAS_Z = 8, + HAS_XYZ = 14, + HAS_GRAPHIC = 16, + HAS_PLANE = 32, + HAS_EVERYTHING = 63, }; private: @@ -60,17 +63,54 @@ public: int64_t id() const { return id_; } int bits() const { return bits_; } - const std::string &action() const { return action_; } + const std::string &action() const { return action_; } double facing() const { return facing_; } - util::XYZ xyz() const { return xyz_; } + 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 std::string &graphic() const { return graphic_; } const std::string &plane() const { return plane_; } - bool has_facing() const { return bits_ & AnimStep::HAS_FACING; } - bool has_xyz() const { return bits_ & AnimStep::HAS_XYZ; } + 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 std::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 std::string &g); + void set_plane(const std::string &p); + + void clear(); + + // 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" + // + void from_lua(lua_State *L, int idx); + + // 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; }; class AnimQueue { @@ -85,37 +125,22 @@ public: size_t size() const { return steps_.size(); } int32_t size_limit() const { return size_limit_; } - // Clear the steps. Doesn't affect size_limit or id. + // Discard all but the most recent N steps. + void keep_only(int n); + + // Clear the steps. + // The resulting steps aren't empty. There will be one + // step in the queue, which will be on the plane "", at (0,0,0). + // Doesn't affect size_limit. void clear_steps(); - // Mutators to create new steps from C++ - // - void add(int64_t id, const std::string &action); - void set_facing(float f); - void set_xyz(util::XYZ xyz); - void set_graphic(const std::string &g); - void set_plane(const std::string &p); - + // Mutator to create a new step. + void add(int64_t id, const AnimStep &step); + // Serialize or deserialize to a StreamBuffer void serialize(StreamBuffer *sb); void deserialize(StreamBuffer *sb); - // Mutator to create new steps from lua. - // - // 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 - // dx: delta-x - // dy: delta-y - // dz: delta-z - // graphic: "graphic" - // plane: "plane" - // - void add(int64_t id, lua_State *L, int idx); - // Get the final resting place after all animations are complete. const AnimStep &back() const; diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index 8e79ee13..c559796c 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -175,7 +175,7 @@ void LuaStack::getclass(LuaSlot classtab, LuaSlot classname) const { } else { LS.set(classtab, classname); if (!LS.istable(classtab)) { - luaL_error(L_, "parameter must be a class"); + luaL_error(L_, "class can be a string or a table"); } LS.rawget(cname, classtab, "__class"); if (!LS.isstring(cname)) { diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index e752cbb9..e8f95541 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -76,8 +76,11 @@ void Tangible::be_a_player() { if (!id_player_pool_.fifo_enabled()) { id_player_pool_.enable_fifo(); - anim_queue_.add(world_->id_global_pool_.get_one(), ""); - anim_queue_.set_graphic("player"); + AnimStep asinit; + asinit.set_graphic("player"); + anim_queue_.add(world_->id_global_pool_.get_one(), asinit); + anim_queue_.keep_only(1); + update_plane_item(); LuaVar classtab, mt, place, tangibles; LuaStack LS(world_->state(), classtab, mt, place, tangibles); @@ -466,7 +469,9 @@ LuaDefine(tangible_animate, "c") { World *w = World::fetch_global_pointer(L); Tangible *tan = w->tangible_get(L, tanobj.index()); int64_t id = w->id_global_pool_.alloc_id_for_thread(L); - tan->anim_queue_.add(id, L, config.index()); + AnimStep step; + step.from_lua(L, config.index()); + tan->anim_queue_.add(id, step); tan->update_plane_item(); return LS.result(); } @@ -484,6 +489,8 @@ LuaDefine(tangible_setclass, "c") { } LuaDefine(tangible_delete, "c") { + // TODO: we need some sanity checks to make sure you're not deleting + // the current player, or anything like that. LuaArg tanobj; LuaStack LS(L, tanobj); World *w = World::fetch_global_pointer(L); @@ -492,9 +499,49 @@ LuaDefine(tangible_delete, "c") { return LS.result(); } -LuaDefine(tangible_make, "c") { - World::fetch_global_pointer(L)->tangible_make(L, 0, true); - return 1; +LuaDefine(tangible_build, "c") { + LuaArg config; + LuaVar classname, classtab, mt; + LuaRet database; + LuaStack LS(L, config, classname, classtab, database, mt); + + LS.checktable(config); + // Get the class of the new tangible. + LS.rawget(classname, config, "class"); + if (LS.isnil(classname)) { + luaL_error(L, "must specify a class name"); + } + LS.getclass(classtab, classname); + + // Parse the initial animation step. + AnimStep initstep; + initstep.from_lua(L, config.index()); + if (!initstep.has_xyz()) { + luaL_error(L, "You must specify (X,Y,Z) for new tangible"); + } + if (!initstep.has_plane()) { + luaL_error(L, "You must specify plane for new tangible"); + } + if (!initstep.has_graphic()) { + luaL_error(L, "You must specify graphic for new tangible"); + } + + // TODO: generate error if there's extra crap in the config table. + + World *w = World::fetch_global_pointer(L); + Tangible *tan = w->tangible_make(L, 0, true); + lua_replace(L, database.index()); + + // Update the class of the new tangible. + LS.getmetatable(mt, database); + LS.rawset(mt, "__index", classtab); + + // Update the animation queue and planemap of the new tangible. + int64_t stepid = w->id_global_pool_.alloc_id_for_thread(L); + tan->anim_queue_.add(stepid, initstep); + tan->update_plane_item(); + + return LS.result(); } LuaDefine(tangible_get, "c") {