aboutsummaryrefslogtreecommitdiff
path: root/src/script/cpp_api/s_entity.cpp
blob: ab8c6ee9521de5a6e6a05098da9ad9e46ed5cce1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "cpp_api/s_entity.h"
#include "cpp_api/s_internal.h"
#include "log.h"
#include "object_properties.h"
#include "common/c_converter.h"
#include "common/c_content.h"

bool ScriptApiEntity::luaentity_Add(u16 id, const char *name)
{
	SCRIPTAPI_PRECHECKHEADER

	verbosestream<<"scriptapi_luaentity_add: id="<<id<<" name=\""
			<<name<<"\""<<std::endl;

	// Get core.registered_entities[name]
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_entities");
	luaL_checktype(L, -1, LUA_TTABLE);
	lua_pushstring(L, name);
	lua_gettable(L, -2);
	// Should be a table, which we will use as a prototype
	//luaL_checktype(L, -1, LUA_TTABLE);
	if (lua_type(L, -1) != LUA_TTABLE){
		errorstream<<"LuaEntity name \""<<name<<"\" not defined"<<std::endl;
		return false;
	}
	int prototype_table = lua_gettop(L);
	//dump2(L, "prototype_table");

	// Create entity object
	lua_newtable(L);
	int object = lua_gettop(L);

	// Set object metatable
	lua_pushvalue(L, prototype_table);
	lua_setmetatable(L, -2);

	// Add object reference
	// This should be userdata with metatable ObjectRef
	objectrefGet(id);
	luaL_checktype(L, -1, LUA_TUSERDATA);
	if (!luaL_checkudata(L, -1, "ObjectRef"))
		luaL_typerror(L, -1, "ObjectRef");
	lua_setfield(L, -2, "object");

	// core.luaentities[id] = object
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "luaentities");
	luaL_checktype(L, -1, LUA_TTABLE);
	lua_pushnumber(L, id); // Push id
	lua_pushvalue(L, object); // Copy object to top of stack
	lua_settable(L, -3);

	return true;
}

void ScriptApiEntity::luaentity_Activate(u16 id,
		const std::string &staticdata, u32 dtime_s)
{
	SCRIPTAPI_PRECHECKHEADER

	verbosestream << "scriptapi_luaentity_activate: id=" << id << std::endl;

	// Get core.luaentities[id]
	luaentity_get(L, id);
	int object = lua_gettop(L);

	// Get on_activate function
	lua_getfield(L, -1, "on_activate");
	if (!lua_isnil(L, -1)) {
		luaL_checktype(L, -1, LUA_TFUNCTION);
		lua_pushvalue(L, object); // self
		lua_pushlstring(L, staticdata.c_str(), staticdata.size());
		lua_pushinteger(L, dtime_s);
		// Call with 3 arguments, 0 results
		if (lua_pcall(L, 3, 0, m_errorhandler))
			scriptError();
	} else {
		lua_pop(L, 1);
	}
	lua_pop(L, 1); // Pop object
}

void ScriptApiEntity::luaentity_Remove(u16 id)
{
	SCRIPTAPI_PRECHECKHEADER

	verbosestream << "scriptapi_luaentity_rm: id=" << id << std::endl;

	// Get core.luaentities table
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "luaentities");
	luaL_checktype(L, -1, LUA_TTABLE);
	int objectstable = lua_gettop(L);

	// Set luaentities[id] = nil
	lua_pushnumber(L, id); // Push id
	lua_pushnil(L);
	lua_settable(L, objectstable);

	lua_pop(L, 2); // pop luaentities, core
}

std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id)
{
	SCRIPTAPI_PRECHECKHEADER

	//infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl;

	// Get core.luaentities[id]
	luaentity_get(L, id);
	int object = lua_gettop(L);

	// Get get_staticdata function
	lua_getfield(L, -1, "get_staticdata");
	if (lua_isnil(L, -1)) {
		lua_pop(L, 2); // Pop entity and  get_staticdata
		return "";
	}

	luaL_checktype(L, -1, LUA_TFUNCTION);
	lua_pushvalue(L, object); // self
	// Call with 1 arguments, 1 results
	if (lua_pcall(L, 1, 1, m_errorhandler))
		scriptError();
	lua_remove(L, object); // Remove object

	size_t len = 0;
	const char *s = lua_tolstring(L, -1, &len);
	lua_pop(L, 1); // Pop static data
	return std::string(s, len);
}

void ScriptApiEntity::luaentity_GetProperties(u16 id,
		ObjectProperties *prop)
{
	SCRIPTAPI_PRECHECKHEADER

	//infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl;

	// Get core.luaentities[id]
	luaentity_get(L, id);

	// Set default values that differ from ObjectProperties defaults
	prop->hp_max = 10;

	/* Read stuff */

	prop->hp_max = getintfield_default(L, -1, "hp_max", 10);

	getboolfield(L, -1, "physical", prop->physical);
	getboolfield(L, -1, "collide_with_objects", prop->collideWithObjects);

	getfloatfield(L, -1, "weight", prop->weight);

	lua_getfield(L, -1, "collisionbox");
	if (lua_istable(L, -1))
		prop->collisionbox = read_aabb3f(L, -1, 1.0);
	lua_pop(L, 1);

	getstringfield(L, -1, "visual", prop->visual);

	getstringfield(L, -1, "mesh", prop->mesh);

	// Deprecated: read object properties directly
	read_object_properties(L, -1, prop);

	// Read initial_properties
	lua_getfield(L, -1, "initial_properties");
	read_object_properties(L, -1, prop);
	lua_pop(L, 1);
}

void ScriptApiEntity::luaentity_Step(u16 id, float dtime)
{
	SCRIPTAPI_PRECHECKHEADER

	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;

	// Get core.luaentities[id]
	luaentity_get(L, id);
	int object = lua_gettop(L);
	// State: object is at top of stack
	// Get step function
	lua_getfield(L, -1, "on_step");
	if (lua_isnil(L, -1)) {
		lua_pop(L, 2); // Pop on_step and entity
		return;
	}
	luaL_checktype(L, -1, LUA_TFUNCTION);
	lua_pushvalue(L, object); // self
	lua_pushnumber(L, dtime); // dtime
	// Call with 2 arguments, 0 results
	if (lua_pcall(L, 2, 0, m_errorhandler))
		scriptError();
	lua_pop(L, 1); // Pop object
}

// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch,
//                       tool_capabilities, direction)
void ScriptApiEntity::luaentity_Punch(u16 id,
		ServerActiveObject *puncher, float time_from_last_punch,
		const ToolCapabilities *toolcap, v3f dir)
{
	SCRIPTAPI_PRECHECKHEADER

	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;

	// Get core.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_punch");
	if (lua_isnil(L, -1)) {
		lua_pop(L, 2); // Pop on_punch and entitu
		return;
	}
	luaL_checktype(L, -1, LUA_TFUNCTION);
	lua_pushvalue(L, object);                // self
	objectrefGetOrCreate(puncher);           // Clicker reference
	lua_pushnumber(L, time_from_last_punch);
	push_tool_capabilities(L, *toolcap);
	push_v3f(L, dir);
	// Call with 5 arguments, 0 results
	if (lua_pcall(L, 5, 0, m_errorhandler))
		scriptError();
	lua_pop(L, 1); // Pop object
}

// Calls entity:on_rightclick(ObjectRef clicker)
void ScriptApiEntity::luaentity_Rightclick(u16 id,
		ServerActiveObject *clicker)
{
	SCRIPTAPI_PRECHECKHEADER

	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;

	// Get core.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");
	if (lua_isnil(L, -1)) {
		lua_pop(L, 2); // Pop on_rightclick and entity
		return;
	}
	luaL_checktype(L, -1, LUA_TFUNCTION);
	lua_pushvalue(L, object); // self
	objectrefGetOrCreate(clicker); // Clicker reference
	// Call with 2 arguments, 0 results
	if (lua_pcall(L, 2, 0, m_errorhandler))
		scriptError();
	lua_pop(L, 1); // Pop object
}

{ std::ostringstream os; os<<m_count; return os.str(); } virtual bool addableTo(InventoryItem *other) { if(std::string(other->getName()) != "CraftItem") return false; CraftItem *m = (CraftItem*)other; if(m->m_subname != m_subname) return false; return true; } u16 freeSpace() { if(m_count > QUANTITY_ITEM_MAX_COUNT) return 0; return QUANTITY_ITEM_MAX_COUNT - m_count; } /* Special methods */ std::string getSubName() { return m_subname; } private: std::string m_subname; }; class ToolItem : public InventoryItem { public: ToolItem(std::string toolname, u16 wear): InventoryItem(1) { m_toolname = toolname; m_wear = wear; } /* Implementation interface */ virtual const char* getName() const { return "ToolItem"; } virtual void serialize(std::ostream &os) { os<<getName(); os<<" "; os<<m_toolname; os<<" "; os<<m_wear; } virtual InventoryItem* clone() { return new ToolItem(m_toolname, m_wear); } #ifndef SERVER video::ITexture * getImage() { if(g_texturesource == NULL) return NULL; std::string basename; if(m_toolname == "WPick") basename = "tool_wpick.png"; else if(m_toolname == "STPick") basename = "tool_stpick.png"; else if(m_toolname == "MesePick") basename = "tool_mesepick.png"; else basename = "cloud.png"; /* Calculate a progress value with sane amount of maximum states */ u32 maxprogress = 30; u32 toolprogress = (65535-m_wear)/(65535/maxprogress); float value_f = (float)toolprogress / (float)maxprogress; std::ostringstream os; os<<basename<<"^[progressbar"<<value_f; return g_texturesource->getTextureRaw(os.str()); /*TextureSpec spec; spec.addTid(g_irrlicht->getTextureId(basename)); spec.addTid(g_irrlicht->getTextureId(os.str())); return g_irrlicht->getTexture(spec);*/ } #endif std::string getText() { return ""; /*std::ostringstream os; u16 f = 4; u16 d = 65535/f; u16 i; for(i=0; i<(65535-m_wear)/d; i++) os<<'X'; for(; i<f; i++) os<<'-'; return os.str();*/ /*std::ostringstream os; os<<m_toolname; os<<" "; os<<(m_wear/655); return os.str();*/ } /* Special methods */ std::string getToolName() { return m_toolname; } u16 getWear() { return m_wear; } // Returns true if weared out bool addWear(u16 add) { if(m_wear >= 65535 - add) { m_wear = 65535; return true; } else { m_wear += add; return false; } } private: std::string m_toolname; u16 m_wear; }; class InventoryList { public: InventoryList(std::string name, u32 size); ~InventoryList(); void clearItems(); void serialize(std::ostream &os); void deSerialize(std::istream &is); InventoryList(const InventoryList &other); InventoryList & operator = (const InventoryList &other); std::string getName(); u32 getSize(); // Count used slots u32 getUsedSlots(); // Get pointer to item InventoryItem * getItem(u32 i); // Returns old item (or NULL). Parameter can be NULL. InventoryItem * changeItem(u32 i, InventoryItem *newitem); // Delete item void deleteItem(u32 i); // Adds an item to a suitable place. Returns leftover item. // If all went into the list, returns NULL. InventoryItem * addItem(InventoryItem *newitem); // If possible, adds item to given slot. // If cannot be added at all, returns the item back. // If can be added partly, decremented item is returned back. // If can be added fully, NULL is returned. InventoryItem * addItem(u32 i, InventoryItem *newitem); // Takes some items from a slot. // If there are not enough, takes as many as it can. // Returns NULL if couldn't take any. InventoryItem * takeItem(u32 i, u32 count); // Decrements amount of every material item void decrementMaterials(u16 count); void print(std::ostream &o); private: core::array<InventoryItem*> m_items; u32 m_size; std::string m_name; }; class Inventory { public: ~Inventory(); void clear(); Inventory(); Inventory(const Inventory &other); Inventory & operator = (const Inventory &other); void serialize(std::ostream &os); void deSerialize(std::istream &is); InventoryList * addList(const std::string &name, u32 size); InventoryList * getList(const std::string &name); bool deleteList(const std::string &name); // A shorthand for adding items. // Returns NULL if the item was fully added, leftover otherwise. InventoryItem * addItem(const std::string &listname, InventoryItem *newitem) { InventoryList *list = getList(listname); if(list == NULL) return newitem; return list->addItem(newitem); } private: // -1 if not found s32 getListIndex(const std::string &name); core::array<InventoryList*> m_lists; }; class Player; struct InventoryContext { Player *current_player; InventoryContext(): current_player(NULL) {} }; class InventoryAction; class InventoryManager { public: InventoryManager(){} virtual ~InventoryManager(){} /* Get a pointer to an inventory specified by id. id can be: - "current_player" - "nodemeta:X,Y,Z" */ virtual Inventory* getInventory(InventoryContext *c, std::string id) {return NULL;} // Used on the server by InventoryAction::apply virtual void inventoryModified(InventoryContext *c, std::string id) {} // Used on the client virtual void inventoryAction(InventoryAction *a) {} }; #define IACTION_MOVE 0 struct InventoryAction { static InventoryAction * deSerialize(std::istream &is); virtual u16 getType() const = 0; virtual void serialize(std::ostream &os) = 0; virtual void apply(InventoryContext *c, InventoryManager *mgr) = 0; }; struct IMoveAction : public InventoryAction { // count=0 means "everything" u16 count; std::string from_inv; std::string from_list; s16 from_i; std::string to_inv; std::string to_list; s16 to_i; IMoveAction() { count = 0; from_i = -1; to_i = -1; } IMoveAction(std::istream &is) { std::string ts; std::getline(is, ts, ' '); count = stoi(ts); std::getline(is, from_inv, ' '); std::getline(is, from_list, ' '); std::getline(is, ts, ' '); from_i = stoi(ts); std::getline(is, to_inv, ' '); std::getline(is, to_list, ' '); std::getline(is, ts, ' '); to_i = stoi(ts); } u16 getType() const { return IACTION_MOVE; } void serialize(std::ostream &os) { os<<"Move "; os<<count<<" "; os<<from_inv<<" "; os<<from_list<<" "; os<<from_i<<" "; os<<to_inv<<" "; os<<to_list<<" "; os<<to_i; } void apply(InventoryContext *c, InventoryManager *mgr); }; #endif