Scripting WIP: dynamic object stuff
authorPerttu Ahola <celeron55@gmail.com>
Sat, 12 Nov 2011 15:37:14 +0000 (17:37 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 29 Nov 2011 17:13:40 +0000 (19:13 +0200)
data/scripts/default.lua
src/content_sao.cpp
src/content_sao.h
src/inventory.cpp
src/inventory.h
src/player.cpp
src/player.h
src/scriptapi.cpp
src/scriptapi.h
src/server.cpp
src/serverobject.h

index 3ec6f84332e6bca72cf10f4adc0565eeff069cbf..dcc58097bc7ae1093a9873e71554111a79997e70 100644 (file)
@@ -162,6 +162,8 @@ end
 -- Called when object is punched
 function TNT:on_punch(hitter)
        print("TNT:on_punch()")
+       self.object:remove()
+       hitter:add_to_inventory("CraftItem testobject1 1")
 end
 
 -- Called when object is right-clicked
index 0abeb0ef047e30675d235f44c72ea96d16681d51..eeb17bd3090975e5ced882359c144dad7f768275 100644 (file)
@@ -237,14 +237,24 @@ InventoryItem * ItemSAO::createInventoryItem()
        }
 }
 
-void ItemSAO::rightClick(Player *player)
+void ItemSAO::punch(ServerActiveObject *puncher)
+{
+       InventoryItem *item = createInventoryItem();
+       bool fits = puncher->addToInventory(item);
+       if(fits)
+               m_removed = true;
+       else
+               delete item;
+}
+
+void ItemSAO::rightClick(ServerActiveObject *clicker)
 {
        infostream<<__FUNCTION_NAME<<std::endl;
        InventoryItem *item = createInventoryItem();
        if(item == NULL)
                return;
        
-       bool to_be_deleted = item->use(m_env, player);
+       bool to_be_deleted = item->use(m_env, clicker);
 
        if(to_be_deleted)
                m_removed = true;
@@ -252,7 +262,7 @@ void ItemSAO::rightClick(Player *player)
                // Reflect changes to the item here
                m_inventorystring = item->getItemString();
        
-       delete item;
+       delete item; // Delete temporary item
 }
 
 /*
@@ -435,11 +445,15 @@ std::string RatSAO::getStaticData()
        return os.str();
 }
 
-InventoryItem* RatSAO::createPickedUpItem()
+void RatSAO::punch(ServerActiveObject *puncher)
 {
        std::istringstream is("CraftItem rat 1", std::ios_base::binary);
        InventoryItem *item = InventoryItem::deSerialize(is);
-       return item;
+       bool fits = puncher->addToInventory(item);
+       if(fits)
+               m_removed = true;
+       else
+               delete item;
 }
 
 /*
@@ -684,9 +698,17 @@ std::string Oerkki1SAO::getStaticData()
        return os.str();
 }
 
-u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir,
-               const std::string &playername)
+void Oerkki1SAO::punch(ServerActiveObject *puncher)
 {
+       v3f dir = (getBasePosition() - puncher->getBasePosition()).normalize();
+
+       std::string toolname = "";
+       InventoryItem *item = puncher->getWieldedItem();
+       if(item && (std::string)item->getName() == "ToolItem"){
+               ToolItem *titem = (ToolItem*)item;
+               toolname = titem->getToolName();
+       }
+
        m_speed_f += dir*12*BS;
 
        u16 amount = 5;
@@ -704,7 +726,8 @@ u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir,
        if(toolname == "SteelPick")
                amount = 7;
        doDamage(amount);
-       return 65536/100;
+       
+       puncher->damageWieldedItem(65536/100);
 }
 
 void Oerkki1SAO::doDamage(u16 d)
@@ -1351,10 +1374,20 @@ void MobV2SAO::step(float dtime, bool send_recommended)
        }
 }
 
-u16 MobV2SAO::punch(const std::string &toolname, v3f dir,
-               const std::string &playername)
+void MobV2SAO::punch(ServerActiveObject *puncher)
 {
-       assert(m_env);
+       v3f dir = (getBasePosition() - puncher->getBasePosition()).normalize();
+
+       std::string toolname = "";
+       InventoryItem *item = puncher->getWieldedItem();
+       if(item && (std::string)item->getName() == "ToolItem"){
+               ToolItem *titem = (ToolItem*)item;
+               toolname = titem->getToolName();
+       }
+       
+       // A quick hack; SAO description is player name for player
+       std::string playername = puncher->getDescription();
+
        Map *map = &m_env->getMap();
        
        actionstream<<playername<<" punches mob id="<<m_id
@@ -1396,7 +1429,8 @@ u16 MobV2SAO::punch(const std::string &toolname, v3f dir,
        if(toolname == "SteelPick")
                amount = 3;
        doDamage(amount);
-       return 65536/100;
+       
+       puncher->damageWieldedItem(65536/100);
 }
 
 bool MobV2SAO::isPeaceful()
@@ -1629,18 +1663,20 @@ InventoryItem* LuaEntitySAO::createPickedUpItem()
        return item;
 }
 
-u16 LuaEntitySAO::punch(const std::string &toolname, v3f dir,
-               const std::string &playername)
+void LuaEntitySAO::punch(ServerActiveObject *puncher)
 {
-       return 0;
+       if(!m_registered)
+               return;
+       lua_State *L = m_env->getLua();
+       scriptapi_luaentity_punch(L, m_id, puncher);
 }
 
-void LuaEntitySAO::rightClick(Player *player)
+void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
 {
        if(!m_registered)
                return;
        lua_State *L = m_env->getLua();
-       scriptapi_luaentity_rightclick_player(L, m_id, player->getName());
+       scriptapi_luaentity_rightclick(L, m_id, clicker);
 }
 
 void LuaEntitySAO::setPos(v3f pos)
index 1d72392324bad3d3b21427569275011556576807..51911fe052fa1be9ca42b5c4524199d013dfecc9 100644 (file)
@@ -50,9 +50,8 @@ public:
        std::string getClientInitializationData();
        std::string getStaticData();
        InventoryItem* createInventoryItem();
-       InventoryItem* createPickedUpItem(){return createInventoryItem();}
-       void rightClick(Player *player);
-
+       void punch(ServerActiveObject *puncher);
+       void rightClick(ServerActiveObject *clicker);
        float getMinimumSavedMovement(){ return 0.1*BS; }
 private:
        std::string m_inventorystring;
@@ -72,7 +71,7 @@ public:
        void step(float dtime, bool send_recommended);
        std::string getClientInitializationData();
        std::string getStaticData();
-       InventoryItem* createPickedUpItem();
+       void punch(ServerActiveObject *puncher);
 private:
        bool m_is_active;
        IntervalLimiter m_inactive_interval;
@@ -98,8 +97,7 @@ public:
        std::string getClientInitializationData();
        std::string getStaticData();
        InventoryItem* createPickedUpItem(){return NULL;}
-       u16 punch(const std::string &toolname, v3f dir,
-                       const std::string &playername);
+       void punch(ServerActiveObject *puncher);
        bool isPeaceful(){return false;}
 private:
        void doDamage(u16 d);
@@ -159,8 +157,7 @@ public:
        std::string getClientInitializationData();
        void step(float dtime, bool send_recommended);
        InventoryItem* createPickedUpItem(){return NULL;}
-       u16 punch(const std::string &toolname, v3f dir,
-                       const std::string &playername);
+       void punch(ServerActiveObject *puncher);
        bool isPeaceful();
 private:
        void sendPosition();
@@ -214,10 +211,8 @@ public:
        std::string getClientInitializationData();
        std::string getStaticData();
        InventoryItem* createPickedUpItem();
-       u16 punch(const std::string &toolname, v3f dir,
-                       const std::string &playername);
-       void rightClick(Player *player);
-
+       void punch(ServerActiveObject *puncher);
+       void rightClick(ServerActiveObject *clicker);
        void setPos(v3f pos);
        void moveTo(v3f pos, bool continuous);
        float getMinimumSavedMovement();
index 45646a69a40791b73e683ad319e917baa5f21954..c9ba9b4e58901512740c66a977f9c80ad0b52f46 100644 (file)
@@ -205,22 +205,23 @@ InventoryItem *CraftItem::createCookResult() const
        return item_craft_create_cook_result(m_subname);
 }
 
-bool CraftItem::use(ServerEnvironment *env, Player *player)
+bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
 {
-       if(item_craft_is_eatable(m_subname))
-       {
-               u16 result_count = getCount() - 1; // Eat one at a time
-               s16 hp_change = item_craft_eat_hp_change(m_subname);
-               if(player->hp + hp_change > 20)
-                       player->hp = 20;
-               else
-                       player->hp += hp_change;
+       if(!item_craft_is_eatable(m_subname))
+               return false;
+       
+       u16 result_count = getCount() - 1; // Eat one at a time
+       s16 hp_change = item_craft_eat_hp_change(m_subname);
+       s16 hp = user->getHP();
+       hp += hp_change;
+       if(hp < 0)
+               hp = 0;
+       user->setHP(hp);
+       
+       if(result_count < 1)
+               return true;
                
-               if(result_count < 1)
-                       return true;
-               else
-                       setCount(result_count);
-       }
+       setCount(result_count);
        return false;
 }
 
index 490cab73ef9eb239ae43241cbdf0954c998b0ea6..dc5b04ff7cc4bfa9053e2427b379fc842b2abc9f 100644 (file)
@@ -36,7 +36,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class ServerActiveObject;
 class ServerEnvironment;
-class Player;
 
 class InventoryItem
 {
@@ -117,7 +116,7 @@ public:
        // Called when item is right-clicked when lying on ground.
        // If returns true, item shall be deleted.
        virtual bool use(ServerEnvironment *env,
-                       Player *player){return false;}
+                       ServerActiveObject *user){return false;}
 
 protected:
        u16 m_count;
@@ -263,7 +262,7 @@ public:
        bool isCookable() const;
        InventoryItem *createCookResult() const;
 
-       bool use(ServerEnvironment *env, Player *player);
+       bool use(ServerEnvironment *env, ServerActiveObject *user);
        
        /*
                Special methods
index 396ce2494537224a794ab32e4cb3f50603ccfdee..f0a395f498e6233d77fd09249c61e83aae667871 100644 (file)
@@ -164,6 +164,70 @@ void Player::deSerialize(std::istream &is)
        inventory.deSerialize(is);
 }
 
+/*
+       ServerRemotePlayer
+*/
+
+/* ServerActiveObject interface */
+
+InventoryItem* ServerRemotePlayer::getWieldedItem()
+{
+       InventoryList *list = inventory.getList("main");
+       if (list)
+               return list->getItem(m_selected_item);
+       return NULL;
+}
+void ServerRemotePlayer::damageWieldedItem(u16 amount)
+{
+       infostream<<"Damaging "<<getName()<<"'s wielded item for amount="
+                       <<amount<<std::endl;
+       InventoryList *list = inventory.getList("main");
+       if(!list)
+               return;
+       InventoryItem *item = list->getItem(m_selected_item);
+       if(item && (std::string)item->getName() == "ToolItem"){
+               ToolItem *titem = (ToolItem*)item;
+               bool weared_out = titem->addWear(amount);
+               if(weared_out)
+                       list->deleteItem(m_selected_item);
+       }
+}
+bool ServerRemotePlayer::addToInventory(InventoryItem *item)
+{
+       infostream<<"Adding "<<item->getName()<<" into "<<getName()
+                       <<"'s inventory"<<std::endl;
+       
+       InventoryList *ilist = inventory.getList("main");
+       if(ilist == NULL)
+               return false;
+       
+       // In creative mode, just delete the item
+       if(g_settings->getBool("creative_mode")){
+               return false;
+       }
+
+       // Skip if inventory has no free space
+       if(ilist->roomForItem(item) == false)
+       {
+               infostream<<"Player inventory has no free space"<<std::endl;
+               return false;
+       }
+
+       // Add to inventory
+       InventoryItem *leftover = ilist->addItem(item);
+       assert(!leftover);
+
+       return true;
+}
+void ServerRemotePlayer::setHP(s16 hp_)
+{
+       hp = hp_;
+}
+s16 ServerRemotePlayer::getHP()
+{
+       return hp;
+}
+
 /*
        RemotePlayer
 */
index c3be0789415ce8b9009d75d96d3a91a3eba8f9e8..e6dfb81991450569753ae626942243dac677e388 100644 (file)
@@ -215,12 +215,20 @@ public:
                Player::setPosition(position);
                ServerActiveObject::setBasePosition(position);
        }
+       
+       /* ServerActiveObject interface */
 
-       /*
-               ServerActiveObject interface
-       */
        u8 getType() const
                {return ACTIVEOBJECT_TYPE_PLAYER;}
+       virtual std::string getDescription(){return getName();}
+       // Returns a reference
+       virtual InventoryItem* getWieldedItem();
+       virtual void damageWieldedItem(u16 amount);
+       // If all fits, eats item and returns true. Otherwise returns false.
+       virtual bool addToInventory(InventoryItem *item);
+       virtual void setHP(s16 hp_);
+       virtual s16 getHP();
+
 private:
 };
 
index f8875c0e36d53e41eb149477d6fa126499013c3b..530c1719e10eb0b6d053a40103e0fe6a5f811107 100644 (file)
@@ -368,6 +368,14 @@ private:
        
        // Exported functions
        
+       // garbage collector
+       static int gc_object(lua_State *L) {
+               ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
+               //infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
+               delete o;
+               return 0;
+       }
+
        // remove(self)
        static int l_remove(lua_State *L)
        {
@@ -427,12 +435,25 @@ private:
                return 0;
        }
 
-       static int gc_object(lua_State *L) {
-               //ObjectRef *o = checkobject(L, 1);
-               ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
-               //infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
-               delete o;
-               return 0;
+       // add_to_inventory(self, itemstring)
+       // returns: true if item was added, false otherwise
+       static int l_add_to_inventory(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               luaL_checkstring(L, 2);
+               ServerActiveObject *co = getobject(ref);
+               if(co == NULL) return 0;
+               // itemstring
+               const char *itemstring = lua_tostring(L, 2);
+               infostream<<"ObjectRef::l_add_to_inventory(): id="<<co->getId()
+                               <<" itemstring=\""<<itemstring<<"\""<<std::endl;
+               // Do it
+               std::istringstream is(itemstring, std::ios::binary);
+               InventoryItem *item = InventoryItem::deSerialize(is);
+               bool fits = co->addToInventory(item);
+               // Return
+               lua_pushboolean(L, fits);
+               return 1;
        }
 
 public:
@@ -502,9 +523,20 @@ const luaL_reg ObjectRef::methods[] = {
        method(ObjectRef, getpos),
        method(ObjectRef, setpos),
        method(ObjectRef, moveto),
+       method(ObjectRef, add_to_inventory),
        {0,0}
 };
 
+// Creates a new anonymous reference if id=0
+static void objectref_get_or_create(lua_State *L, ServerActiveObject *cobj)
+{
+       if(cobj->getId() == 0){
+               ObjectRef::create(L, cobj);
+       } else {
+               objectref_get(L, cobj->getId());
+       }
+}
+
 /*
        Main export function
 */
@@ -570,6 +602,7 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
        lua_setfield(L, -2, "env");
 }
 
+#if 0
 // Dump stack top with the dump2 function
 static void dump2(lua_State *L, const char *name)
 {
@@ -581,6 +614,7 @@ static void dump2(lua_State *L, const char *name)
        if(lua_pcall(L, 2, 0, 0))
                script_error(L, "error: %s\n", lua_tostring(L, -1));
 }
+#endif
 
 /*
        object_reference
@@ -815,8 +849,9 @@ void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime)
                script_error(L, "error running function 'step': %s\n", lua_tostring(L, -1));
 }
 
-void scriptapi_luaentity_rightclick_player(lua_State *L, u16 id,
-               const char *playername)
+// Calls entity:on_punch(ObjectRef puncher)
+void scriptapi_luaentity_punch(lua_State *L, u16 id,
+               ServerActiveObject *puncher)
 {
        realitycheck(L);
        assert(lua_checkstack(L, 20));
@@ -827,12 +862,36 @@ void scriptapi_luaentity_rightclick_player(lua_State *L, u16 id,
        luaentity_get(L, id);
        int object = lua_gettop(L);
        // State: object is at top of stack
-       // Get step function
+       // Get function
+       lua_getfield(L, -1, "on_punch");
+       luaL_checktype(L, -1, LUA_TFUNCTION);
+       lua_pushvalue(L, object); // self
+       objectref_get_or_create(L, puncher); // Clicker reference
+       // Call with 2 arguments, 0 results
+       if(lua_pcall(L, 2, 0, 0))
+               script_error(L, "error running function 'on_punch': %s\n", lua_tostring(L, -1));
+}
+
+// Calls entity:on_rightclick(ObjectRef clicker)
+void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
+               ServerActiveObject *clicker)
+{
+       realitycheck(L);
+       assert(lua_checkstack(L, 20));
+       //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+       StackUnroller stack_unroller(L);
+
+       // Get minetest.luaentities[id]
+       luaentity_get(L, id);
+       int object = lua_gettop(L);
+       // State: object is at top of stack
+       // Get function
        lua_getfield(L, -1, "on_rightclick");
        luaL_checktype(L, -1, LUA_TFUNCTION);
        lua_pushvalue(L, object); // self
-       // Call with 1 arguments, 0 results
-       if(lua_pcall(L, 1, 0, 0))
-               script_error(L, "error running function 'step': %s\n", lua_tostring(L, -1));
+       objectref_get_or_create(L, clicker); // Clicker reference
+       // Call with 2 arguments, 0 results
+       if(lua_pcall(L, 2, 0, 0))
+               script_error(L, "error running function 'on_rightclick': %s\n", lua_tostring(L, -1));
 }
 
index 2a5eb25a96458490310ef21557ed017ac522cfdf..458f90acbb2cc35ec331b8062353de52f59ee227 100644 (file)
@@ -42,8 +42,10 @@ std::string scriptapi_luaentity_get_state(lua_State *L, u16 id);
 void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
                LuaEntityProperties *prop);
 void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime);
-void scriptapi_luaentity_rightclick_player(lua_State *L, u16 id,
-               const char *playername);
+void scriptapi_luaentity_punch(lua_State *L, u16 id,
+               ServerActiveObject *clicker);
+void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
+               ServerActiveObject *clicker);
 
 #endif
 
index 330ce21c2c912cdeccd8e6e8c78d74f7df6c3538..08a26adc6165981f97c4438577c72e38e1c1347f 100644 (file)
@@ -2299,10 +2299,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
                
                //TODO: Check that object is reasonably close
+       
+               // Get ServerRemotePlayer
+               ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+
+               // Update wielded item
+               srp->wieldItem(item_i);
                
-               // Left click, pick object up (usually)
+               // Left click, pick/punch
                if(button == 0)
                {
+                       actionstream<<player->getName()<<" punches object "
+                                       <<obj->getId()<<std::endl;
+                       
+                       // Do stuff
+                       obj->punch(srp);
+                       
+#if 0
                        /*
                                Try creating inventory item
                        */
@@ -2371,6 +2384,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        SendInventory(player->peer_id);
                                }
                        }
+#endif
                }
                // Right click, do something with object
                if(button == 1)
@@ -2378,18 +2392,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        actionstream<<player->getName()<<" right clicks object "
                                        <<obj->getId()<<std::endl;
 
-                       // Track hp changes super-crappily
-                       u16 oldhp = player->hp;
-                       
                        // Do stuff
-                       obj->rightClick(player);
-                       
-                       // Send back stuff
-                       if(player->hp != oldhp)
-                       {
-                               SendPlayerHP(player);
-                       }
+                       obj->rightClick(srp);
                }
+
+               /*
+                       Update player state to client
+               */
+               SendPlayerHP(player);
+               UpdateCrafting(player->peer_id);
+               SendInventory(player->peer_id);
        }
        else if(command == TOSERVER_GROUND_ACTION)
        {
index 66118cca021460985eb146e8702f4e4b51f9200d..7e767188a9f688436755aa21140cf0a65949597c 100644 (file)
@@ -71,6 +71,7 @@ public:
        /*
                Some more dynamic interface
        */
+       
        virtual void setPos(v3f pos)
                { setBasePosition(pos); }
        // continuous: if true, object does not stop immediately at pos
@@ -80,7 +81,11 @@ public:
        // saving to disk may be omitted
        virtual float getMinimumSavedMovement()
                { return 2.0*BS; }
+       
+       virtual bool isPeaceful(){return true;}
 
+       virtual std::string getDescription(){return "SAO";}
+       
        /*
                Step object in time.
                Messages added to messages are sent to client over network.
@@ -106,26 +111,22 @@ public:
        */
        virtual std::string getStaticData(){return "";}
        
-       /*
-               Item that the player gets when this object is picked up.
-               If NULL, object cannot be picked up.
-       */
-       virtual InventoryItem* createPickedUpItem(){return NULL;}
+       virtual void punch(ServerActiveObject *puncher){}
+       virtual void rightClick(ServerActiveObject *clicker){}
        
-       /*
-               If the object doesn't return an item, this will be called.
-               Return value is tool wear.
-       */
-       virtual u16 punch(const std::string &toolname, v3f dir,
-                       const std::string &playername)
-       {return 0;}
-
-       /*
-       */
-       virtual void rightClick(Player *player){}
+       // Returns a reference
+       virtual InventoryItem* getWieldedItem()
+               { return NULL; }
+       virtual void damageWieldedItem(u16 amount)
+               {}
+       // If all fits, eats item and returns true. Otherwise returns false.
+       virtual bool addToInventory(InventoryItem *item)
+               {return false;}
+       virtual void setHP(s16 hp)
+               {}
+       virtual s16 getHP()
+               {return 0;}
 
-       virtual bool isPeaceful(){return true;}
-       
        /*
                Number of players which know about this object. Object won't be
                deleted until this is 0 to keep the id preserved for the right