]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_item.cpp
Add First Person Attachments (#10360)
[minetest.git] / src / script / cpp_api / s_item.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "cpp_api/s_item.h"
21 #include "cpp_api/s_internal.h"
22 #include "common/c_converter.h"
23 #include "common/c_content.h"
24 #include "lua_api/l_item.h"
25 #include "lua_api/l_inventory.h"
26 #include "server.h"
27 #include "log.h"
28 #include "util/pointedthing.h"
29 #include "inventory.h"
30 #include "inventorymanager.h"
31
32 bool ScriptApiItem::item_OnDrop(ItemStack &item,
33                 ServerActiveObject *dropper, v3f pos)
34 {
35         SCRIPTAPI_PRECHECKHEADER
36
37         int error_handler = PUSH_ERROR_HANDLER(L);
38
39         // Push callback function on stack
40         if (!getItemCallback(item.name.c_str(), "on_drop"))
41                 return false;
42
43         // Call function
44         LuaItemStack::create(L, item);
45         objectrefGetOrCreate(L, dropper);
46         pushFloatPos(L, pos);
47         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
48         if (!lua_isnil(L, -1)) {
49                 try {
50                         item = read_item(L, -1, getServer()->idef());
51                 } catch (LuaError &e) {
52                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
53                 }
54         }
55         lua_pop(L, 2);  // Pop item and error handler
56         return true;
57 }
58
59 bool ScriptApiItem::item_OnPlace(ItemStack &item,
60                 ServerActiveObject *placer, const PointedThing &pointed)
61 {
62         SCRIPTAPI_PRECHECKHEADER
63
64         int error_handler = PUSH_ERROR_HANDLER(L);
65
66         // Push callback function on stack
67         if (!getItemCallback(item.name.c_str(), "on_place"))
68                 return false;
69
70         // Call function
71         LuaItemStack::create(L, item);
72
73         if (!placer)
74                 lua_pushnil(L);
75         else
76                 objectrefGetOrCreate(L, placer);
77
78         pushPointedThing(pointed);
79         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
80         if (!lua_isnil(L, -1)) {
81                 try {
82                         item = read_item(L, -1, getServer()->idef());
83                 } catch (LuaError &e) {
84                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
85                 }
86         }
87         lua_pop(L, 2);  // Pop item and error handler
88         return true;
89 }
90
91 bool ScriptApiItem::item_OnUse(ItemStack &item,
92                 ServerActiveObject *user, const PointedThing &pointed)
93 {
94         SCRIPTAPI_PRECHECKHEADER
95
96         int error_handler = PUSH_ERROR_HANDLER(L);
97
98         // Push callback function on stack
99         if (!getItemCallback(item.name.c_str(), "on_use"))
100                 return false;
101
102         // Call function
103         LuaItemStack::create(L, item);
104         objectrefGetOrCreate(L, user);
105         pushPointedThing(pointed);
106         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
107         if(!lua_isnil(L, -1)) {
108                 try {
109                         item = read_item(L, -1, getServer()->idef());
110                 } catch (LuaError &e) {
111                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
112                 }
113         }
114         lua_pop(L, 2);  // Pop item and error handler
115         return true;
116 }
117
118 bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item,
119                 ServerActiveObject *user, const PointedThing &pointed)
120 {
121         SCRIPTAPI_PRECHECKHEADER
122
123         int error_handler = PUSH_ERROR_HANDLER(L);
124
125         if (!getItemCallback(item.name.c_str(), "on_secondary_use"))
126                 return false;
127
128         LuaItemStack::create(L, item);
129         objectrefGetOrCreate(L, user);
130         pushPointedThing(pointed);
131         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
132         if (!lua_isnil(L, -1)) {
133                 try {
134                         item = read_item(L, -1, getServer()->idef());
135                 } catch (LuaError &e) {
136                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
137                 }
138         }
139         lua_pop(L, 2);  // Pop item and error handler
140         return true;
141 }
142
143 bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
144                 const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
145 {
146         SCRIPTAPI_PRECHECKHEADER
147
148         int error_handler = PUSH_ERROR_HANDLER(L);
149
150         lua_getglobal(L, "core");
151         lua_getfield(L, -1, "on_craft");
152         LuaItemStack::create(L, item);
153         objectrefGetOrCreate(L, user);
154
155         // Push inventory list
156         std::vector<ItemStack> items;
157         for (u32 i = 0; i < old_craft_grid->getSize(); i++) {
158                 items.push_back(old_craft_grid->getItem(i));
159         }
160         push_items(L, items);
161
162         InvRef::create(L, craft_inv);
163         PCALL_RES(lua_pcall(L, 4, 1, error_handler));
164         if (!lua_isnil(L, -1)) {
165                 try {
166                         item = read_item(L, -1, getServer()->idef());
167                 } catch (LuaError &e) {
168                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
169                 }
170         }
171         lua_pop(L, 2);  // Pop item and error handler
172         return true;
173 }
174
175 bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
176                 const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
177 {
178         SCRIPTAPI_PRECHECKHEADER
179         sanity_check(old_craft_grid);
180         int error_handler = PUSH_ERROR_HANDLER(L);
181
182         lua_getglobal(L, "core");
183         lua_getfield(L, -1, "craft_predict");
184         LuaItemStack::create(L, item);
185         objectrefGetOrCreate(L, user);
186
187         //Push inventory list
188         std::vector<ItemStack> items;
189         for (u32 i = 0; i < old_craft_grid->getSize(); i++) {
190                 items.push_back(old_craft_grid->getItem(i));
191         }
192         push_items(L, items);
193
194         InvRef::create(L, craft_inv);
195         PCALL_RES(lua_pcall(L, 4, 1, error_handler));
196         if (!lua_isnil(L, -1)) {
197                 try {
198                         item = read_item(L, -1, getServer()->idef());
199                 } catch (LuaError &e) {
200                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
201                 }
202         }
203         lua_pop(L, 2);  // Pop item and error handler
204         return true;
205 }
206
207 // Retrieves core.registered_items[name][callbackname]
208 // If that is nil or on error, return false and stack is unchanged
209 // If that is a function, returns true and pushes the
210 // function onto the stack
211 // If core.registered_items[name] doesn't exist, core.nodedef_default
212 // is tried instead so unknown items can still be manipulated to some degree
213 bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname,
214                 const v3s16 *p)
215 {
216         lua_State* L = getStack();
217
218         lua_getglobal(L, "core");
219         lua_getfield(L, -1, "registered_items");
220         lua_remove(L, -2); // Remove core
221         luaL_checktype(L, -1, LUA_TTABLE);
222         lua_getfield(L, -1, name);
223         lua_remove(L, -2); // Remove registered_items
224         // Should be a table
225         if (lua_type(L, -1) != LUA_TTABLE) {
226                 // Report error and clean up
227                 errorstream << "Item \"" << name << "\" not defined";
228                 if (p)
229                         errorstream << " at position " << PP(*p);
230                 errorstream << std::endl;
231                 lua_pop(L, 1);
232
233                 // Try core.nodedef_default instead
234                 lua_getglobal(L, "core");
235                 lua_getfield(L, -1, "nodedef_default");
236                 lua_remove(L, -2);
237                 luaL_checktype(L, -1, LUA_TTABLE);
238         }
239
240         setOriginFromTable(-1);
241
242         lua_getfield(L, -1, callbackname);
243         lua_remove(L, -2); // Remove item def
244         // Should be a function or nil
245         if (lua_type(L, -1) == LUA_TFUNCTION) {
246                 return true;
247         }
248
249         if (!lua_isnil(L, -1)) {
250                 errorstream << "Item \"" << name << "\" callback \""
251                         << callbackname << "\" is not a function" << std::endl;
252         }
253         lua_pop(L, 1);
254         return false;
255 }
256
257 void ScriptApiItem::pushPointedThing(const PointedThing &pointed, bool hitpoint)
258 {
259         lua_State* L = getStack();
260
261         push_pointed_thing(L, pointed, false, hitpoint);
262 }
263