1 #include "lua_hooks.h"
2 #include "special_owners.h"
3 #include "mrt/logger.h"
4 #include "mrt/random.h"
5 #include "object.h"
6 #include "world.h"
7 #include "resource_manager.h"
8 #include "game_monitor.h"
9 #include "player_manager.h"
10 #include "player_slot.h"
11 #include "tmx/map.h"
12 #include "sound/mixer.h"
13 #include "game.h"
14 #include <assert.h>
15 #include <stdexcept>
16 #include "var.h"
17 #include "config.h"
18 #include "clunk/object.h"
19 
20 #define LUA_TRY try
21 #define LUA_CATCH(where) catch(const std::exception &e) {\
22 		lua_pushstring(L, e.what());\
23 		lua_error(L);\
24 		return 0;\
25 	} catch(...) {\
26 		lua_pushstring(L, "unknown exception");\
27 		lua_error(L);\
28 		return 0;\
29 	}
30 
31 static std::string next_map;
32 
getNextMap()33 const std::string & LuaHooks::getNextMap() { return next_map; }
resetNextMap()34 void LuaHooks::resetNextMap() { next_map.clear(); }
35 
36 
lua_hooks_print(lua_State * L)37 static int lua_hooks_print(lua_State *L) {
38 LUA_TRY {
39 	int n = lua_gettop(L);
40 	std::string str;
41 	for (int i = 1; i <= n; i++) {
42 		const char *v = lua_tostring(L, i);
43 		str += v?v: "(nil)";
44 		str += '\t';
45 	}
46 	LOG_DEBUG(("[lua] %s", str.c_str()));
47 
48 	return 0;
49 } LUA_CATCH("lua_hooks_print")
50 }
51 
52 
lua_hooks_spawn_random(lua_State * L)53 static int lua_hooks_spawn_random(lua_State *L) {
54 LUA_TRY {
55 	int args = lua_gettop(L);
56 	if (args < 2) {
57 		lua_pushstring(L, "spawn_random requires object and animation");
58 		lua_error(L);
59 		return 0;
60 	}
61 	const char *object = lua_tostring(L, 1), *animation = lua_tostring(L, 2);
62 	if (object == NULL)
63 		throw_ex(("object argument could not be converted to string"));
64 	if (animation == NULL)
65 		throw_ex(("animation argument could not be converted to string"));
66 	Object *obj = ResourceManager->createObject(object, animation);
67 
68 	//Matrix<int> matrix;
69 	//World->get_impassability_matrix(matrix, obj, NULL);
70 	const Matrix<int> &matrix = Map->get_impassability_matrix(0);
71 
72 	const v2<int> tile_size = Map->getPathTileSize();
73 	if (obj->size.is0())
74 		throw_ex(("object size must not be 0,0"));
75 
76 	v2<int> obj_size = ((obj->size.convert<int>() - 1) / tile_size) + 1;
77 	LOG_DEBUG(("searching random %dx%d spot", obj_size.x, obj_size.y));
78 
79 	int w = matrix.get_width(), h = matrix.get_height();
80 	std::vector<v2<int> > spots;
81 	for(int y = 0; y < h - obj_size.y + 1; ++y)
82 		for(int x= 0; x < w - obj_size.x + 1; ++x) {
83 			for(int yy = 0; yy < obj_size.y; ++yy)
84 				for(int xx = 0; xx < obj_size.x; ++xx) {
85 					int im = matrix.get(y + yy, x + xx);
86 					if (im < 0 || im >= 100)
87 						goto skip;
88 
89 				}
90 			spots.push_back(v2<int>(x, y));
91 		skip: ;
92 		}
93 
94 	size_t n = spots.size();
95 	if (n == 0)
96 		throw_ex(("no spots found"));
97 
98 	int idx = mrt::random(n);
99 	LOG_DEBUG(("found %u spots. get #%d", (unsigned)n, idx));
100 	v2<float> pos = (spots[idx] * tile_size).convert<float>();
101 
102 	//LOG_DEBUG(("map : %s", matrix.dump().c_str()));
103 	obj_size = tile_size * obj_size / 2;
104 	World->addObject(obj, pos + obj_size.convert<float>() - obj->size / 2);
105 
106 	lua_pushinteger(L, obj->get_id());
107 	return 1;
108 } LUA_CATCH("lua_hooks_spawn_random")
109 }
110 
lua_hooks_map_size(lua_State * L)111 static int lua_hooks_map_size(lua_State *L) {
112 LUA_TRY {
113 	v2<int> map_size = Map->get_size();
114 	lua_pushinteger(L, map_size.x);
115 	lua_pushinteger(L, map_size.y);
116 	return 2;
117 } LUA_CATCH("lua_hooks_map_size")
118 }
119 
lua_hooks_load_map(lua_State * L)120 static int lua_hooks_load_map(lua_State *L) {
121 LUA_TRY {
122 	int n = lua_gettop(L);
123 	if (n < 1) {
124 		lua_pushstring(L, "load_map requires map name");
125 		lua_error(L);
126 		return 0;
127 	}
128 	const char * name = lua_tostring(L, 1);
129 	if (name == NULL)
130 		throw_ex(("load_map's 1st argument is not a string"));
131 	next_map = name;
132 	return 0;
133 } LUA_CATCH("lua_hooks_load_map")
134 }
135 
136 
lua_hooks_object_exists(lua_State * L)137 static int lua_hooks_object_exists(lua_State *L) {
138 LUA_TRY {
139 	int n = lua_gettop(L);
140 	if (n < 1) {
141 		lua_pushstring(L, "object_exists requires object id");
142 		lua_error(L);
143 		return 0;
144 	}
145 	int id = lua_tointeger(L, 1);
146 	const Object *o = World->getObjectByID(id);
147 
148 	bool strict = (n >= 2)? lua_toboolean(L, 2) != 0: false;
149 
150 	bool exists = o?!o->is_dead():false;
151 	if (exists && !strict && o->get_state() == "broken")
152 		exists = false;
153 
154 	lua_pushboolean(L, exists?1:0);
155 	return 1;
156 } LUA_CATCH("lua_hooks_object_exists")
157 }
158 
lua_hooks_object_property(lua_State * L)159 static int lua_hooks_object_property(lua_State *L) {
160 LUA_TRY {
161 	int n = lua_gettop(L);
162 	if (n < 2) {
163 		lua_pushstring(L, "object_property requires object id and property name");
164 		lua_error(L);
165 		return 0;
166 	}
167 	int id = lua_tointeger(L, 1);
168 	const Object *o = World->getObjectByID(id);
169 	if (o == NULL) {
170 		lua_pushnil(L);
171 		return 1;
172 	}
173 	const char *cprop = lua_tostring(L, 2);
174 	if (cprop == NULL)
175 		throw_ex(("property argument could not be converted to string"));
176 
177 	std::string prop = cprop;
178 	if (prop == "classname") {
179 		lua_pushstring(L, o->classname.c_str());
180 		return 1;
181 	} else if (prop == "registered_name") {
182 		lua_pushstring(L, o->registered_name.c_str());
183 		return 1;
184 	} else if (prop == "animation") {
185 		lua_pushstring(L, o->animation.c_str());
186 		return 1;
187 	} else if (prop == "hp") {
188 		lua_pushinteger(L, o->hp);
189 		return 1;
190 	}
191 
192 	lua_pushstring(L, mrt::format_string("object_property: unknown property %s", prop.c_str()).c_str());
193 	lua_error(L);
194 	return 0;
195 
196 } LUA_CATCH("lua_hooks_object_property")
197 }
198 
lua_hooks_set_object_property(lua_State * L)199 static int lua_hooks_set_object_property(lua_State *L) {
200 LUA_TRY {
201 	int n = lua_gettop(L);
202 	if (n < 3) {
203 		lua_pushstring(L, "object_property requires object id, property name and value");
204 		lua_error(L);
205 		return 0;
206 	}
207 	int id = lua_tointeger(L, 1);
208 	Object *o = World->getObjectByID(id);
209 	if (o == NULL) {
210 		return 0;
211 	}
212 	const char *cprop = lua_tostring(L, 2);
213 	if (cprop == NULL)
214 		throw_ex(("property argument could not be converted to string"));
215 
216 	std::string prop = cprop;
217 	if (prop == "animation") {
218 		const char *value = lua_tostring(L, 3);
219 		if (value == NULL)
220 			throw_ex(("property value for '%s' could not be converted to string", cprop));
221 
222 		o->init(value);
223 		return 0;
224 	}
225 
226 	lua_pushstring(L, mrt::format_string("set_object_property: unknown property %s", prop.c_str()).c_str());
227 	lua_error(L);
228 	return 0;
229 
230 } LUA_CATCH("lua_hooks_set_object_property")
231 }
232 
lua_hooks_set_slot_property(lua_State * L)233 static int lua_hooks_set_slot_property(lua_State *L) {
234 LUA_TRY {
235 	int n = lua_gettop(L);
236 	if (n < 3) {
237 		lua_pushstring(L, "set_slot_property requires object id, property name and property value");
238 		lua_error(L);
239 		return 0;
240 	}
241 	int id = lua_tointeger(L, 1);
242 	if (id < 1)
243 		throw_ex(("slot #%d is invalid", id));
244 	PlayerSlot &slot =  PlayerManager->get_slot(id - 1);
245 
246 	const char *cprop = lua_tostring(L, 2);
247 	if (cprop == NULL)
248 		throw_ex(("property argument could not be converted to string"));
249 
250 	std::string prop = cprop;
251 
252 	if (prop == "classname") {
253 		const char *value = lua_tostring(L, 3);
254 		if (value == NULL)
255 			throw_ex(("`value' argument could not be converted to string"));
256 
257 		slot.classname = value;
258 		return 0;
259 	} else if (prop == "animation") {
260 		const char *value = lua_tostring(L, 3);
261 		if (value == NULL)
262 			throw_ex(("`value' argument could not be converted to string"));
263 
264 		slot.animation = value;
265 		return 0;
266 	} else if (prop == "spawn_limit") {
267 		slot.spawn_limit = lua_tointeger(L, 3);
268 		return 0;
269 	}
270 
271 	lua_pushstring(L, mrt::format_string("slot_property: unknown property %s", prop.c_str()).c_str());
272 	lua_error(L);
273 	return 0;
274 
275 } LUA_CATCH("slot_property")
276 }
277 
lua_hooks_display_hint(lua_State * L)278 static int lua_hooks_display_hint(lua_State *L) {
279 LUA_TRY {
280 	int n = lua_gettop(L);
281 	if (n < 3) {
282 		lua_pushstring(L, "display_hint requires slot_id, area and message-id");
283 		lua_error(L);
284 		return 0;
285 	}
286 	int id = lua_tointeger(L, 1);
287 	if (id < 1)
288 		throw_ex(("slot #%d is invalid", id));
289 	PlayerSlot &slot =  PlayerManager->get_slot(id - 1);
290 
291 	const char *area = lua_tostring(L, 2);
292 	if (area == NULL)
293 		throw_ex(("area argument could not be converted to string"));
294 
295 	const char *message = lua_tostring(L, 3);
296 	if (message == NULL)
297 		throw_ex(("message-id argument could not be converted to string"));
298 
299 	slot.displayTooltip(area, message);
300 
301 	return 0;
302 } LUA_CATCH("display_hint")
303 }
304 
lua_hooks_remove_hints(lua_State * L)305 static int lua_hooks_remove_hints(lua_State *L) {
306 LUA_TRY {
307 	int n = lua_gettop(L);
308 	if (n < 1) {
309 		lua_pushstring(L, "remove_hints requires slot_id");
310 		lua_error(L);
311 		return 0;
312 	}
313 	int id = lua_tointeger(L, 1);
314 	if (id < 1)
315 		throw_ex(("slot #%d is invalid", id));
316 	PlayerSlot &slot =  PlayerManager->get_slot(id - 1);
317 	slot.removeTooltips();
318 
319 	return 0;
320 } LUA_CATCH("remove_hints")
321 }
322 
lua_hooks_slot_property(lua_State * L)323 static int lua_hooks_slot_property(lua_State *L) {
324 LUA_TRY {
325 	int n = lua_gettop(L);
326 	if (n < 2) {
327 		lua_pushstring(L, "slot_property requires object id and property name");
328 		lua_error(L);
329 		return 0;
330 	}
331 	int id = lua_tointeger(L, 1);
332 	if (id < 1)
333 		throw_ex(("slot #%d is invalid", id));
334 	PlayerSlot &slot =  PlayerManager->get_slot(id - 1);
335 
336 	const char *cprop = lua_tostring(L, 2);
337 	if (cprop == NULL)
338 		throw_ex(("name could not be converted to string"));
339 
340 	const std::string prop = cprop;
341 	if (prop == "classname") {
342 		lua_pushstring(L, slot.classname.c_str());
343 		return 1;
344 	} else if (prop == "animation") {
345 		lua_pushstring(L, slot.animation.c_str());
346 		return 1;
347 	} else if (prop == "spawn_limit") {
348 		lua_pushinteger(L, slot.spawn_limit);
349 		return 1;
350 	} else if (prop == "id") {
351 		lua_pushinteger(L, slot.id);
352 		return 1;
353 	}
354 
355 	lua_pushstring(L, mrt::format_string("object_property: unknown property %s", prop.c_str()).c_str());
356 	lua_error(L);
357 	return 0;
358 
359 } LUA_CATCH("slot_property")
360 }
361 
lua_hooks_kill_object(lua_State * L)362 static int lua_hooks_kill_object(lua_State *L) {
363 	LUA_TRY {
364 		int n = lua_gettop(L);
365 		if (n < 1) {
366 			lua_pushstring(L, "kill object requres object id as first argument");
367 			lua_error(L);
368 			return 0;
369 		}
370 		int id = lua_tointeger(L, 1);
371 		bool system = (n >= 2)? lua_toboolean(L, 2) != 0: false;
372 
373 		Object *o = World->getObjectByID(id);
374 		if (o == NULL)
375 			return 0;
376 
377 		if (system) {
378 			o->Object::emit("death", NULL);
379 		} else {
380 			o->emit("death", NULL);
381 		}
382 		return 0;
383 	} LUA_CATCH("kill_object")
384 }
385 
386 
lua_hooks_show_item(lua_State * L)387 static int lua_hooks_show_item(lua_State *L) {
388 	LUA_TRY {
389 		int n = lua_gettop(L);
390 		if (n < 1) {
391 			lua_pushstring(L, "show_item requires item's property as first argument");
392 			lua_error(L);
393 			return 0;
394 		}
395 		const char *prop = lua_tostring(L, 1);
396 		if (prop == NULL) {
397 			lua_pushstring(L, "show_item's first argument must be string");
398 			lua_error(L);
399 			return 0;
400 		}
401 		GameItem &item = GameMonitor->find(prop);
402 		if (item.hidden || World->getObjectByID(item.id) == NULL)
403 			item.respawn();
404 
405 		lua_pushinteger(L, item.id);
406 		return 1;
407 	} LUA_CATCH("show_item")
408 }
409 
lua_hooks_kill_item(lua_State * L)410 static int lua_hooks_kill_item(lua_State *L) {
411 	LUA_TRY {
412 		int n = lua_gettop(L);
413 		if (n < 1) {
414 			lua_pushstring(L, "kill_item requires item's property as first argument");
415 			lua_error(L);
416 			return 0;
417 		}
418 		const char *prop = lua_tostring(L, 1);
419 		if (prop == NULL) {
420 			lua_pushstring(L, "kill_item's first argument must be string");
421 			lua_error(L);
422 			return 0;
423 		}
424 		GameItem &item = GameMonitor->find(prop);
425 		Object *o = World->getObjectByID(item.id);
426 		if (o != NULL && !o->is_dead())
427 			o->emit("death", NULL);
428 		return 0;
429 	} LUA_CATCH("kill_item")
430 }
431 
lua_hooks_play_tune(lua_State * L)432 static int lua_hooks_play_tune(lua_State *L) {
433 	LUA_TRY {
434 		int n = lua_gettop(L);
435 		if (n < 1) {
436 			lua_pushstring(L, "play_tune requre tune name");
437 			lua_error(L);
438 			return 0;
439 		}
440 		const char *name = lua_tostring(L, 1);
441 		if (name == NULL) {
442 			lua_pushstring(L, "tune name must be string");
443 			lua_error(L);
444 			return 0;
445 		}
446 		bool loop = true;
447 		if (n >= 2) {
448 			loop = lua_toboolean(L, 2) != 0;
449 		}
450 
451 		Mixer->play(name, loop);
452 		return 0;
453 	} LUA_CATCH("play_tune")
454 }
455 
lua_hooks_reset_tune(lua_State * L)456 static int lua_hooks_reset_tune(lua_State *L) {
457 	LUA_TRY {
458 		Mixer->reset();
459 		return 0;
460 	} LUA_CATCH("reset_tune")
461 }
462 
lua_hooks_hide_item(lua_State * L)463 static int lua_hooks_hide_item(lua_State *L) {
464 	LUA_TRY {
465 		int n = lua_gettop(L);
466 		if (n < 1) {
467 			lua_pushstring(L, "hide_item requires item's property as first argument");
468 			lua_error(L);
469 			return 0;
470 		}
471 		const char *prop = lua_tostring(L, 1);
472 		if (prop == NULL) {
473 			lua_pushstring(L, "hide_item's first argument must be string");
474 			lua_error(L);
475 			return 0;
476 		}
477 		GameItem &item = GameMonitor->find(prop);
478 		item.hidden = true;
479 		item.kill();
480 
481 		return 0;
482 	} LUA_CATCH("hide_item")
483 }
484 
lua_hooks_item_exists(lua_State * L)485 static int lua_hooks_item_exists(lua_State *L) {
486 	LUA_TRY {
487 		int n = lua_gettop(L);
488 		if (n < 1) {
489 			lua_pushstring(L, "item_exists requires item's property as first argument");
490 			lua_error(L);
491 			return 0;
492 		}
493 		const char *prop = lua_tostring(L, 1);
494 		if (prop == NULL) {
495 			lua_pushstring(L, "item_exists' first argument must be string");
496 			lua_error(L);
497 			return 0;
498 		}
499 		bool strict = (n >= 2)? lua_toboolean(L, 2) != 0: false;
500 
501 		GameItem &item = GameMonitor->find(prop);
502 		const Object *o = World->getObjectByID(item.id);
503 
504 		bool exists = o?!o->is_dead():false;
505 		if (exists && !strict && o->get_state() == "broken")
506 			exists = false;
507 
508 		lua_pushboolean(L, exists?1:0);
509 		return 1;
510 	} LUA_CATCH("item_exists")
511 }
512 
513 
lua_hooks_spawn(lua_State * L)514 static int lua_hooks_spawn(lua_State *L) {
515 	LUA_TRY {
516 		int n = lua_gettop(L);
517 		if (n < 4) {
518 			lua_pushstring(L, "spawn() requires at least 4 arguments: classname, animation, x, y");
519 			lua_error(L);
520 			return 0;
521 		}
522 		const char *classname = lua_tostring(L, 1);
523 		if (classname == NULL) {
524 			lua_pushstring(L, "spawn: first argument must be string");
525 			lua_error(L);
526 			return 0;
527 		}
528 		const char *animation = lua_tostring(L, 2);
529 		if (animation == NULL) {
530 			lua_pushstring(L, "spawn: first argument must be string");
531 			lua_error(L);
532 			return 0;
533 		}
534 
535 		int x = lua_tointeger(L, 3);
536 		int y = lua_tointeger(L, 4);
537 
538 		Object *o = ResourceManager->createObject(classname, animation);
539 	//	if (z)
540 	//		o->set_z(z, true);
541 		o->add_owner(OWNER_MAP);
542 
543 	//	if (dir)
544 	//		o->set_direction(dir);
545 
546 		World->addObject(o, v2<float>(x, y) - o->size / 2);
547 		lua_pushinteger(L, o->get_id());
548 		return 1;
549 	} LUA_CATCH("spawn")
550 }
551 
lua_hooks_game_over(lua_State * L)552 static int lua_hooks_game_over(lua_State *L) {
553 	LUA_TRY {
554 		int n = lua_gettop(L);
555 		if (n < 4) {
556 			lua_pushstring(L, "game_over() requires at least 4 arguments: area, message, time and win");
557 			lua_error(L);
558 			return 0;
559 		}
560 
561 		const char *area = lua_tostring(L, 1);
562 		if (area == NULL) {
563 			lua_pushstring(L, "game_over: first argument must be string");
564 			lua_error(L);
565 			return 0;
566 		}
567 
568 		const char *message = lua_tostring(L, 2);
569 		if (message == NULL) {
570 			lua_pushstring(L, "game_over: second argument must be string");
571 			lua_error(L);
572 			return 0;
573 		}
574 		lua_Number time = lua_tonumber(L, 3);
575 		bool win = lua_toboolean(L, 4) != 0;
576 		GameMonitor->game_over(area, message, (float)time, win);
577 	} LUA_CATCH("game_over")
578 	return 0;
579 }
580 
lua_hooks_set_timer(lua_State * L)581 static int lua_hooks_set_timer(lua_State *L) {
582 	LUA_TRY {
583 		int n = lua_gettop(L);
584 		if (n < 4) {
585 			lua_pushstring(L, "set_timer: requires at least 4 arguments: area, message, time and win");
586 			lua_error(L);
587 			return 0;
588 		}
589 
590 		const char *area = lua_tostring(L, 1);
591 		if (area == NULL) {
592 			lua_pushstring(L, "set_timer: first argument must be string");
593 			lua_error(L);
594 			return 0;
595 		}
596 
597 		const char *message = lua_tostring(L, 2);
598 		if (message == NULL) {
599 			lua_pushstring(L, "set_timer: second argument must be string");
600 			lua_error(L);
601 			return 0;
602 		}
603 
604 		lua_Number time = lua_tonumber(L, 3);
605 		bool win = lua_toboolean(L, 4) != 0;
606 		GameMonitor->setTimer(area, message, (float)time, win);
607 	} LUA_CATCH("set_timer")
608 	return 0;
609 }
610 
lua_hooks_reset_timer(lua_State * L)611 static int lua_hooks_reset_timer(lua_State *L) {
612 	LUA_TRY {
613 		GameMonitor->resetTimer();
614 	} LUA_CATCH("reset_timer")
615 	return 0;
616 }
617 
618 
lua_hooks_display_message(lua_State * L)619 static int lua_hooks_display_message(lua_State *L) {
620 	LUA_TRY {
621 		int n = lua_gettop(L);
622 		if (n < 4) {
623 			lua_pushstring(L, "display_message: requires at least 4 arguments: area, message, time and global");
624 			lua_error(L);
625 			return 0;
626 		}
627 
628 		const char *area = lua_tostring(L, 1);
629 		if (area == NULL) {
630 			lua_pushstring(L, "display_message: first argument must be string");
631 			lua_error(L);
632 			return 0;
633 		}
634 
635 		const char *message = lua_tostring(L, 2);
636 		if (message == NULL) {
637 			lua_pushstring(L, "display_message: second argument must be string");
638 			lua_error(L);
639 			return 0;
640 		}
641 		lua_Number time = lua_tonumber(L, 3);
642 		bool global = lua_toboolean(L, 4) != 0;
643 		GameMonitor->displayMessage(area, message, (float)time, global);
644 	} LUA_CATCH("display_message")
645 	return 0;
646 }
647 
lua_hooks_hide_message(lua_State * L)648 static int lua_hooks_hide_message(lua_State *L) {
649 	LUA_TRY {
650 		GameMonitor->hideMessage();
651 	} LUA_CATCH("hide_message")
652 	return 0;
653 }
654 
lua_hooks_damage_map(lua_State * L)655 static int lua_hooks_damage_map(lua_State *L) {
656 	LUA_TRY {
657 		int n = lua_gettop(L);
658 		if (n < 3) {
659 			lua_pushstring(L, "damage map: requires at least 3 arguments: x, y and hp");
660 			lua_error(L);
661 			return 0;
662 		}
663 		float x = (float)lua_tonumber(L, 1);
664 		float y = (float)lua_tonumber(L, 2);
665 		int hp = lua_tointeger(L, 3);
666 		float r = 0;
667 		if (n > 3)
668 			r = (float)lua_tonumber(L, 4);
669 
670 		if (r > 0)
671 			Map->damage(v2<float>(x, y), hp, r);
672 		else
673 			Map->damage(v2<float>(x, y), hp);
674 	} LUA_CATCH("damage_map")
675 	return 0;
676 }
677 
lua_hooks_enable_ai(lua_State * L)678 static int lua_hooks_enable_ai(lua_State *L) {
679 	LUA_TRY {
680 		int n = lua_gettop(L);
681 		if (n < 1) {
682 			lua_pushstring(L, "enable_ai: requires classname");
683 			lua_error(L);
684 			return 0;
685 		}
686 		const char *classname = lua_tostring(L, 1);
687 		if (classname == NULL) {
688 			lua_pushstring(L, "enable_ai: first argument must be string");
689 			lua_error(L);
690 			return 0;
691 		}
692 		GameMonitor->disable(classname, false);
693 	} LUA_CATCH("enable_ai")
694 	return 0;
695 }
696 
lua_hooks_disable_ai(lua_State * L)697 static int lua_hooks_disable_ai(lua_State *L) {
698 	LUA_TRY {
699 		int n = lua_gettop(L);
700 		if (n < 1) {
701 			lua_pushstring(L, "disable_ai: requires classname");
702 			lua_error(L);
703 			return 0;
704 		}
705 		const char *classname = lua_tostring(L, 1);
706 		if (classname == NULL) {
707 			lua_pushstring(L, "disable_ai: first argument must be string");
708 			lua_error(L);
709 			return 0;
710 		}
711 		GameMonitor->disable(classname, true);
712 	} LUA_CATCH("disable_ai")
713 	return 0;
714 }
715 
lua_hooks_visual_effect(lua_State * L)716 static int lua_hooks_visual_effect(lua_State *L) {
717 	LUA_TRY {
718 		int n = lua_gettop(L);
719 		if (n < 2) {
720 			lua_pushstring(L, "visual_effect: requires name and duration");
721 			lua_error(L);
722 			return 0;
723 		}
724 		const char *name = lua_tostring(L, 1);
725 		if (name == NULL) {
726 			lua_pushstring(L, "visual_effect: first argument must be a string");
727 			lua_error(L);
728 			return 0;
729 		}
730 		float d = (float)lua_tonumber(L, 2);
731 		std::string effect = name;
732 
733 		if (effect == "shaking") {
734 			int i = (n >= 3)?lua_tointeger(L, 3) : 4;
735 
736 			Game->shake(d, i);
737 		} else throw_ex(("unknown visual effect name: %s", name));
738 	} LUA_CATCH("visual_effect")
739 	return 0;
740 }
741 
lua_hooks_add_effect(lua_State * L)742 static int lua_hooks_add_effect(lua_State *L) {
743 LUA_TRY {
744 	int n = lua_gettop(L);
745 	if (n < 3) {
746 		lua_pushstring(L, "add_effect requires object id, effect name and duration");
747 		lua_error(L);
748 		return 0;
749 	}
750 
751 	int id = lua_tointeger(L, 1);
752 	Object *o = World->getObjectByID(id);
753 
754 	if (o == NULL) {
755 		return 0;
756 	}
757 
758 	const char * effect = lua_tostring(L, 2);
759 	if (effect == NULL)
760 		throw_ex(("effect name could not be converted to string"));
761 	float duration = lua_tonumber(L, 3);
762 	LOG_DEBUG(("adding effect %s for %g seconds", effect, duration));
763 
764 	o->add_effect(effect, duration);
765 	return 0;
766 } LUA_CATCH("add_effect")
767 }
768 
lua_hooks_remove_effect(lua_State * L)769 static int lua_hooks_remove_effect(lua_State *L) {
770 LUA_TRY {
771 	int n = lua_gettop(L);
772 	if (n < 2) {
773 		lua_pushstring(L, "add_effect requires object id and effect name.");
774 		lua_error(L);
775 		return 0;
776 	}
777 
778 	int id = lua_tointeger(L, 1);
779 	Object *o = World->getObjectByID(id);
780 
781 	if (o == NULL) {
782 		return 0;
783 	}
784 
785 	const char * effect = lua_tostring(L, 2);
786 	if (effect == NULL)
787 		throw_ex(("effect name could not be converted to string"));
788 
789 	o->remove_effect(effect);
790 	return 0;
791 } LUA_CATCH("remove_effect")
792 }
793 
lua_hooks_add_waypoint_object(lua_State * L)794 static int lua_hooks_add_waypoint_object(lua_State *L) {
795 LUA_TRY {
796 	int n = lua_gettop(L);
797 	if (n < 2) {
798 		lua_pushstring(L, "set_waypoint requires source object id and destination object id");
799 		lua_error(L);
800 		return 0;
801 	}
802 
803 	int src_id = lua_tointeger(L, 1);
804 	int dst_id = lua_tointeger(L, 2);
805 	Object *src = World->getObjectByID(src_id);
806 	const Object *dst = World->getObjectByID(dst_id);
807 	if (src == NULL || dst == NULL) {
808 		if (src == NULL)
809 			LOG_ERROR(("object %d does not exists", src_id));
810 		if (dst == NULL)
811 			LOG_ERROR(("object %d does not exists", dst_id));
812 		return 0;
813 	}
814 
815 	v2<int> dst_pos;
816 	dst->get_center_position(dst_pos);
817 
818 	Way way;
819 	way.push_back(dst_pos);
820 
821 	src->set_way(way);
822 	return 0;
823 } LUA_CATCH("add_waypoint_object")
824 }
825 
lua_hooks_add_waypoints(lua_State * L)826 static int lua_hooks_add_waypoints(lua_State *L) {
827 LUA_TRY {
828 	int n = lua_gettop(L);
829 	if (n < 2 || !lua_istable(L, 2)) {
830 		lua_pushstring(L, "add_waypoints requires object id and array");
831 		lua_error(L);
832 		return 0;
833 	}
834 	int id = lua_tointeger(L, 1);
835 	Object *o = World->getObjectByID(id);
836 	if (o == NULL)
837 		return 0;
838 
839 	Way way;
840 
841 	lua_pushnil(L);  /* first key */
842     while (lua_next(L, 2) != 0) {
843     	int idx = lua_gettop(L);
844 
845 		lua_pushnil(L);
846 		std::vector<int> pos;
847 		while(lua_next(L, idx)) {
848 			pos.push_back(lua_tointeger(L, -1));
849 			lua_pop(L, 1);
850 		}
851 
852 		//LOG_DEBUG(("pos.size = %u", (unsigned)pos.size()));
853 		if (pos.size() < 2)
854 			throw_ex(("invalid waypoint on position %u", (unsigned)way.size()));
855 
856 		way.push_back(v2<int>(pos[0], pos[1]));
857 		lua_pop(L, 1);  /* removes `value'; keeps `key' for next iteration */
858 	}
859 
860 	o->set_way(way);
861 
862 	return 0;
863 } LUA_CATCH("add_waypoints")
864 }
865 
866 
lua_hooks_has_waypoints(lua_State * L)867 static int lua_hooks_has_waypoints(lua_State *L) {
868 LUA_TRY {
869 	int n = lua_gettop(L);
870 	if (n < 1) {
871 		lua_pushstring(L, "has_waypoints requires object id");
872 		lua_error(L);
873 		return 0;
874 	}
875 	int id = lua_tointeger(L, 1);
876 	Object *o = World->getObjectByID(id);
877 	lua_pushboolean(L, o != NULL && o->is_driven()? 1:0);
878 	return 1;
879 } LUA_CATCH("has_waypoints")
880 }
881 
lua_hooks_set_config_override(lua_State * L)882 static int lua_hooks_set_config_override(lua_State *L) {
883 LUA_TRY {
884 	int n = lua_gettop(L);
885 	if (n < 2) {
886 		lua_pushstring(L, "set_config_override requires key name and override value");
887 		lua_error(L);
888 		return 0;
889 	}
890 	const char * name = lua_tostring(L, 1);
891 	const char *value = lua_tostring(L, 2);
892 	if (name == NULL || value == NULL) {
893 		lua_pushstring(L, mrt::format_string("set_config_override: %s argument must be a string", (name == NULL)?"first":"second").c_str());
894 		lua_error(L);
895 		return 0;
896 	}
897 
898 	Var var;
899 	var.fromString(value);
900 	Config->setOverride(name, var);
901 	Config->invalidateCachedValues();
902 
903 } LUA_CATCH("set_config_override")
904 	return 0;
905 }
906 
lua_hooks_play_sound(lua_State * L)907 static int lua_hooks_play_sound(lua_State *L) {
908 LUA_TRY {
909 	int n = lua_gettop(L);
910 	if (n < 2) {
911 		lua_pushstring(L, "play_sound requires object_id(0 == listener), sound and optionally loop flag and gain level. ");
912 		lua_error(L);
913 		return 0;
914 	}
915 	int object_id = lua_tointeger(L, 1);
916 	Object *o = NULL;
917 	if (object_id > 0) {
918 		o = World->getObjectByID(object_id);
919 		if (o == NULL)
920 			throw_ex(("object with id %d not found", object_id));
921 	}
922 
923 	const char * name = lua_tostring(L, 2);
924 	if (name == NULL) {
925 		lua_pushstring(L, "play_sound: second argument(sound name) must be a string");
926 		lua_error(L);
927 		return 0;
928 	}
929 
930 	bool loop = false;
931 	float gain = 1.0f;
932 	if (n >= 3)
933 		loop = lua_toboolean(L, 3) != 0;
934 	if (n >= 4)
935 		gain = lua_tonumber(L, 4);
936 	Mixer->playSample(o, name, loop, gain);
937 
938 } LUA_CATCH("play_sound")
939 	return 0;
940 }
941 
lua_hooks_stop_sound(lua_State * L)942 static int lua_hooks_stop_sound(lua_State *L) {
943 LUA_TRY {
944 	int n = lua_gettop(L);
945 	if (n < 1) {
946 		lua_pushstring(L, "stop_sound requires object_id(0 == listener) and sound. ");
947 		lua_error(L);
948 		return 0;
949 	}
950 	int object_id = lua_tointeger(L, 1);
951 	const Object *o = NULL;
952 	if (object_id > 0) {
953 		o = World->getObjectByID(object_id);
954 		if (o == NULL)
955 			throw_ex(("object with id %d not found", object_id));
956 	}
957 
958 	const char * name = NULL;
959 	if (n >= 2) {
960 		name = lua_tostring(L, 2);
961 		if (name == NULL) {
962 			lua_pushstring(L, "stop_sound: second argument(sound name) must be a string");
963 			lua_error(L);
964 			return 0;
965 		}
966 	}
967 
968 	clunk::Object *co = o->get_clunk_object();
969 	if (co == NULL)
970 		return 0;
971 
972 	if (name == NULL)
973 		co->cancel_all();
974 	else
975 		co->cancel(name);
976 
977 } LUA_CATCH("stop_sound")
978 	return 0;
979 }
980 
981 
lua_hooks_random(lua_State * L)982 static int lua_hooks_random(lua_State *L) {
983 LUA_TRY {
984 	int n = lua_gettop(L);
985 	if (n < 1) {
986 		lua_pushstring(L, "random requires upper limit value");
987 		lua_error(L);
988 		return 0;
989 	}
990 	n = lua_tointeger(L, 1);
991 	lua_pushinteger(L, mrt::random(n));
992 	return 1;
993 } LUA_CATCH("random")
994 }
995 
996 
lua_hooks_players_number(lua_State * L)997 static int lua_hooks_players_number(lua_State *L) {
998 	int pn = (int)PlayerManager->get_slots_count();
999 
1000 	int n = lua_gettop(L);
1001 	if (n >= 1) {
1002 		bool active = lua_toboolean(L, 1) != 0;
1003 		if (active)
1004 			pn -= PlayerManager->get_free_slots_count();
1005 	}
1006 
1007 	lua_pushinteger(L, pn);
1008 	return 1;
1009 }
1010 
lua_hooks_set_specials(lua_State * L)1011 static int lua_hooks_set_specials(lua_State *L) {
1012 LUA_TRY {
1013 	int n = lua_gettop(L);
1014 	if (n < 1 || !lua_istable(L, 1)) {
1015 		lua_pushstring(L, "set_specials requires table as first argument");
1016 		lua_error(L);
1017 		return 0;
1018 	}
1019 	std::vector<int> specials;
1020 	lua_pushnil(L);  /* first key */
1021     while (lua_next(L, 1) != 0) {
1022 		/* `key' is at index -2 and `value' at index -1 */
1023 		//printf("%s - %s\n", lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)));
1024 		int id = lua_tointeger(L, -1);
1025 		specials.push_back(id);
1026 		lua_pop(L, 1);  /* removes `value'; keeps `key' for next iteration */
1027 	}
1028 	GameMonitor->setSpecials(specials);
1029 	return 0;
1030 } LUA_CATCH("lua_random")
1031 }
1032 
1033 
1034 
lua_hooks_play_animation(lua_State * L)1035 static int lua_hooks_play_animation(lua_State *L) {
1036 LUA_TRY {
1037 	int n = lua_gettop(L);
1038 	if (n < 2) {
1039 		lua_pushstring(L, "play_animation requires object id, pose name and optional loop/mode flag");
1040 		lua_error(L);
1041 		return 0;
1042 	}
1043 	int id = lua_tointeger(L, 1);
1044 	Object *o = World->getObjectByID(id);
1045 	if (o == NULL)
1046 		return 0;
1047 
1048 	const char *pose = lua_tostring(L, 2);
1049 	if (pose == NULL)
1050 		throw_ex(("pose name could not be converted to string"));
1051 
1052 	if (n > 2) {
1053 		bool loop = lua_toboolean(L, 3) != 0;
1054 		o->play(pose, loop);
1055 	} else {
1056 		o->play_now(pose);
1057 	}
1058 	return 0;
1059 } LUA_CATCH("play_animation")
1060 }
1061 
lua_hooks_cancel_animation(lua_State * L)1062 static int lua_hooks_cancel_animation(lua_State *L) {
1063 LUA_TRY {
1064 	int n = lua_gettop(L);
1065 	if (n < 1) {
1066 		lua_pushstring(L, "cancel_animation requires object id, and optional mode(0 - current, 1 - all, 2 - repeatable)");
1067 		lua_error(L);
1068 		return 0;
1069 	}
1070 	int id = lua_tointeger(L, 1);
1071 	Object *o = World->getObjectByID(id);
1072 	if (o == NULL)
1073 		return 0;
1074 
1075 	int mode = n > 1 ? lua_tointeger(L, 2): 0;
1076 	switch(mode) {
1077 	case 0:
1078 		o->cancel();
1079 		break;
1080 	case 1:
1081 		o->cancel_all();
1082 		break;
1083 	case 2:
1084 		o->cancel_repeatable();
1085 		break;
1086 	default:
1087 		throw_ex(("invalid mode %d", mode));
1088 	}
1089 
1090 	return 0;
1091 } LUA_CATCH("cancel_animation")
1092 }
1093 
lua_hooks_get_state(lua_State * L)1094 static int lua_hooks_get_state(lua_State *L) {
1095 LUA_TRY {
1096 	int n = lua_gettop(L);
1097 	if (n < 1) {
1098 		lua_pushstring(L, "get_state requires object id");
1099 		lua_error(L);
1100 		return 0;
1101 	}
1102 	int id = lua_tointeger(L, 1);
1103 
1104 	Object *o = World->getObjectByID(id);
1105 	lua_pushstring(L, o != NULL? o->get_state().c_str(): "");
1106 	return 1;
1107 } LUA_CATCH("get_state")
1108 }
1109 
lua_hooks_start_timer(lua_State * L)1110 static int lua_hooks_start_timer(lua_State *L) {
1111 LUA_TRY {
1112 	int n = lua_gettop(L);
1113 	if (n < 2) {
1114 		lua_pushstring(L, "start_timer requires timer-name, period and optional repeat flag (default -> false)");
1115 		lua_error(L);
1116 		return 0;
1117 	}
1118 	const char *name = lua_tostring(L, 1);
1119 	if (name == NULL) {
1120 		lua_pushstring(L, "start_timer: could not convert first argument to string.");
1121 		lua_error(L);
1122 		return 0;
1123 	}
1124 	const float period = lua_tonumber(L, 2);
1125 	const bool repeat = n >= 3? (lua_toboolean(L, 3) != 0) : false;
1126 	GameMonitor->startGameTimer(name, period, repeat);
1127 	return 0;
1128 } LUA_CATCH("lua_hooks_start_timer")
1129 }
1130 
lua_hooks_stop_timer(lua_State * L)1131 static int lua_hooks_stop_timer(lua_State *L) {
1132 LUA_TRY {
1133 	int n = lua_gettop(L);
1134 	if (n < 1) {
1135 		lua_pushstring(L, "stop_timer requires timer-name");
1136 		lua_error(L);
1137 		return 0;
1138 	}
1139 	const char *name = lua_tostring(L, 1);
1140 	if (name == NULL) {
1141 		lua_pushstring(L, "stop_timer: could not convert first argument to string.");
1142 		lua_error(L);
1143 		return 0;
1144 	}
1145 	GameMonitor->stopGameTimer(name);
1146 	return 0;
1147 } LUA_CATCH("lua_hooks_stop_timer")
1148 }
1149 
lua_hooks_group_add(lua_State * L)1150 static int lua_hooks_group_add(lua_State *L) {
1151 LUA_TRY {
1152 	int n = lua_gettop(L);
1153 	if (n < 4) {
1154 		lua_pushstring(L, "group_add requires object id, group-object-name, classname and animation");
1155 		lua_error(L);
1156 		return 0;
1157 	}
1158 	int id = lua_tointeger(L, 1);
1159 	Object *o = World->getObjectByID(id);
1160 	if (o == NULL)
1161 		return 0;
1162 
1163 	const char *name = lua_tostring(L, 2);
1164 	const char *cname = lua_tostring(L, 3);
1165 	const char *aname = lua_tostring(L, 4);
1166 	if (name == NULL || cname == NULL || aname == NULL)
1167 		throw_ex(("name: %s, cname: %s, aname: %s: some argument(s) cannot be converted", name, cname, aname));
1168 
1169 	Object *child = o->add(name, cname, aname, v2<float>(), Centered);
1170 	lua_pushinteger(L, child->get_id());
1171 	return 1;
1172 } LUA_CATCH("group_add")
1173 }
1174 
lua_hooks_group_has(lua_State * L)1175 static int lua_hooks_group_has(lua_State *L) {
1176 LUA_TRY {
1177 	int n = lua_gettop(L);
1178 	if (n < 2) {
1179 		lua_pushstring(L, "group_has requires object id and group-object-name");
1180 		lua_error(L);
1181 		return 0;
1182 	}
1183 
1184 	int id = lua_tointeger(L, 1);
1185 	Object *o = World->getObjectByID(id);
1186 	if (o == NULL) {
1187 		lua_pushinteger(L, 0);
1188 		return 1;
1189 	}
1190 
1191 	const char *name = lua_tostring(L, 2);
1192 	if (name == NULL)
1193 		throw_ex(("name cannot be converted to the string"));
1194 
1195 	lua_pushinteger(L, o->has(name)? o->get(name)->get_id(): 0);
1196 	return 1;
1197 } LUA_CATCH("group_has")
1198 }
1199 
lua_hooks_group_remove(lua_State * L)1200 static int lua_hooks_group_remove(lua_State *L) {
1201 LUA_TRY {
1202 	int n = lua_gettop(L);
1203 	if (n < 2) {
1204 		lua_pushstring(L, "group_remove requires object id and group-object-name");
1205 		lua_error(L);
1206 		return 0;
1207 	}
1208 	int id = lua_tointeger(L, 1);
1209 	Object *o = World->getObjectByID(id);
1210 	if (o == NULL) {
1211 		return 0;
1212 	}
1213 
1214 	const char *name = lua_tostring(L, 2);
1215 	if (name == NULL)
1216 		throw_ex(("name cannot be converted to the string"));
1217 
1218 	o->remove(name);
1219 	return 0;
1220 } LUA_CATCH("group_remove")
1221 }
1222 
1223 #include "campaign.h"
1224 
lua_hooks_get_difficulty(lua_State * L)1225 static int lua_hooks_get_difficulty(lua_State *L) {
1226 	LUA_TRY {
1227 		const Campaign *campaign = GameMonitor->getCampaign();
1228 		if (campaign == NULL)
1229 			throw_ex(("get_difficulty could be used only from campaign script"));
1230 
1231 		std::string profile;
1232 		Config->get("engine.profile", profile, std::string());
1233 
1234 		int difficulty;
1235 		Config->get("campaign." + profile + "." + campaign->name + ".difficulty", difficulty, 1);
1236 		lua_pushinteger(L, difficulty);
1237 		return 1;
1238 	} LUA_CATCH("get_difficulty");
1239 }
1240 
1241 #include "finder.h"
1242 
load(const std::string & name)1243 void LuaHooks::load(const std::string &name) {
1244 	LOG_DEBUG(("loading lua code from %s...", name.c_str()));
1245 
1246 	mrt::Chunk data;
1247 	Finder->load(data, name, false);
1248 	std::string::size_type p = name.find('/');
1249 	state.load(p != std::string::npos? name.substr(p + 1): name, data);
1250 
1251 //Utility:
1252 	lua_register(state, "print", lua_hooks_print);
1253 	lua_register(state, "random", lua_hooks_random);
1254 
1255 //Game flow / messages / timers
1256 	lua_register(state, "game_over", lua_hooks_game_over);
1257 	lua_register(state, "display_message", lua_hooks_display_message);
1258 	lua_register(state, "hide_message", lua_hooks_hide_message);
1259 	lua_register(state, "set_timer", lua_hooks_set_timer);
1260 	lua_register(state, "reset_timer", lua_hooks_reset_timer);
1261 	lua_register(state, "damage_map", lua_hooks_damage_map);
1262 	lua_register(state, "load_map", lua_hooks_load_map);
1263 	lua_register(state, "visual_effect", lua_hooks_visual_effect);
1264 	lua_register(state, "set_config_override", lua_hooks_set_config_override);
1265 	lua_register(state, "map_size", lua_hooks_map_size);
1266 	lua_register(state, "set_specials", lua_hooks_set_specials);
1267 
1268 //low level timer
1269 	lua_register(state, "start_timer", lua_hooks_start_timer);
1270 	lua_register(state, "stop_timer", lua_hooks_stop_timer);
1271 
1272 //Sound
1273 	lua_register(state, "play_sound", lua_hooks_play_sound);
1274 	lua_register(state, "stop_sound", lua_hooks_stop_sound);
1275 	lua_register(state, "play_tune", lua_hooks_play_tune);
1276 	lua_register(state, "reset_tune", lua_hooks_reset_tune);
1277 
1278 //Players management
1279 	lua_register(state, "players_number", lua_hooks_players_number);
1280 	lua_register(state, "set_slot_property", lua_hooks_set_slot_property);
1281 	lua_register(state, "slot_property", lua_hooks_slot_property);
1282 	lua_register(state, "display_hint", lua_hooks_display_hint);
1283 	lua_register(state, "remove_hints", lua_hooks_remove_hints);
1284 
1285 //Items management:
1286 	lua_register(state, "item_exists", lua_hooks_item_exists);
1287 	lua_register(state, "show_item", lua_hooks_show_item);
1288 	lua_register(state, "hide_item", lua_hooks_hide_item);
1289 	lua_register(state, "kill_item", lua_hooks_kill_item);
1290 
1291 //AI related
1292 	lua_register(state, "enable_ai", lua_hooks_enable_ai);
1293 	lua_register(state, "disable_ai", lua_hooks_disable_ai);
1294 	lua_register(state, "add_waypoint_object", lua_hooks_add_waypoint_object);
1295 	lua_register(state, "add_waypoints", lua_hooks_add_waypoints);
1296 	lua_register(state, "has_waypoints", lua_hooks_has_waypoints);
1297 
1298 //Object related functions :
1299 	lua_register(state, "spawn", lua_hooks_spawn);
1300 	lua_register(state, "spawn_random", lua_hooks_spawn_random);
1301 	lua_register(state, "object_exists", lua_hooks_object_exists);
1302 	lua_register(state, "object_property", lua_hooks_object_property);
1303 	lua_register(state, "set_object_property", lua_hooks_set_object_property);
1304 	lua_register(state, "kill_object", lua_hooks_kill_object);
1305 	lua_register(state, "add_effect", lua_hooks_add_effect);
1306 	lua_register(state, "remove_effect", lua_hooks_remove_effect);
1307 	lua_register(state, "play_animation", lua_hooks_play_animation);
1308 	lua_register(state, "cancel_animation", lua_hooks_cancel_animation);
1309 	lua_register(state, "get_state", lua_hooks_get_state);
1310 
1311 //object grouping stuff
1312 	lua_register(state, "group_add", lua_hooks_group_add);
1313 	lua_register(state, "group_has", lua_hooks_group_has);
1314 	lua_register(state, "group_remove", lua_hooks_group_remove);
1315 
1316 	lua_register(state, "get_difficulty", lua_hooks_get_difficulty);
1317 
1318 
1319 	state.call(0, LUA_MULTRET);
1320 
1321 	has_on_tick = check_function("on_tick");
1322 	has_on_spawn = check_function("on_spawn");
1323 	has_on_load = check_function("on_load");
1324 	has_on_tooltip = check_function("on_tooltip");
1325 	has_on_timer = check_function("on_timer");
1326 }
1327 
check_function(const std::string & name)1328 bool LuaHooks::check_function(const std::string &name) {
1329 	lua_settop(state, 0);
1330 
1331 	lua_getglobal(state, name.c_str());
1332 	bool r = !(lua_isnoneornil(state, -1));
1333 
1334 	LOG_DEBUG(("checking for function: %s: %c", name.c_str(), r?'+':'-'));
1335 	lua_pop(state, 1);
1336 
1337 	return r;
1338 }
1339 
on_spawn(const std::string & classname,const std::string & animation,const std::string & property)1340 const bool LuaHooks::on_spawn(const std::string &classname, const std::string &animation, const std::string &property) {
1341 	if (!has_on_spawn)
1342 		return true;
1343 
1344 	lua_settop(state, 0);
1345 
1346 	lua_getglobal(state, "on_spawn");
1347 	lua_pushstring(state, classname.c_str());
1348 	lua_pushstring(state, animation.c_str());
1349 	lua_pushstring(state, property.c_str());
1350 
1351 	state.call(3, 1);
1352 	bool r = lua_toboolean(state, 1) != 0;
1353 	lua_pop(state, 1);
1354 	LOG_DEBUG(("on spawn returns %s", r?"true":"false"));
1355 
1356 	return r;
1357 }
1358 
on_load()1359 void LuaHooks::on_load() {
1360 	if (!has_on_load)
1361 		return;
1362 
1363 	lua_settop(state, 0);
1364 
1365 	LOG_DEBUG(("calling on_load()"));
1366 	lua_getglobal(state, "on_load");
1367 	state.call(0, 0);
1368 }
1369 
1370 
on_tick(const float dt)1371 void LuaHooks::on_tick(const float dt) {
1372 	if (!has_on_tick)
1373 		return;
1374 
1375 	lua_settop(state, 0);
1376 
1377 	lua_getglobal(state, "on_tick");
1378 	lua_pushnumber(state, dt);
1379 
1380 	state.call(1, 0);
1381 }
1382 
on_tooltip(const std::string & event,const int slot_id,const std::string & area,const std::string & message)1383 void LuaHooks::on_tooltip(const std::string &event, const int slot_id, const std::string & area, const std::string & message) {
1384 	if (!has_on_tooltip)
1385 		return;
1386 
1387 	lua_settop(state, 0);
1388 
1389 	lua_getglobal(state, "on_tooltip");
1390 
1391 	lua_pushstring(state, event.c_str());
1392 	lua_pushinteger(state, slot_id + 1);
1393 	lua_pushstring(state, area.c_str());
1394 	lua_pushstring(state, message.c_str());
1395 
1396 	state.call(4, 0);
1397 }
1398 
on_timer(const std::string & name)1399 void LuaHooks::on_timer(const std::string &name) {
1400 	if (!has_on_timer)
1401 		return;
1402 
1403 	lua_getglobal(state, "on_timer");
1404 	lua_pushstring(state, name.c_str());
1405 	state.call(1, 0);
1406 }
1407 
call(const std::string & method)1408 void LuaHooks::call(const std::string &method) {
1409 	LOG_DEBUG(("calling %s()", method.c_str()));
1410 	lua_settop(state, 0);
1411 
1412 	lua_getglobal(state, method.c_str());
1413 	state.call(0, 0);
1414 }
1415 
call1(const std::string & method,const int id)1416 void LuaHooks::call1(const std::string &method, const int id) {
1417 	LOG_DEBUG(("calling %s(%d)", method.c_str(), id));
1418 	lua_settop(state, 0);
1419 
1420 	lua_getglobal(state, method.c_str());
1421 	lua_pushinteger(state, id);
1422 
1423 	state.call(1, 0);
1424 }
1425 
clear()1426 void LuaHooks::clear() {
1427 	state.clear();
1428 	has_on_tick = has_on_spawn = has_on_load = has_on_tooltip = has_on_timer = false;
1429 }
1430 
LuaHooks()1431 LuaHooks::LuaHooks() : has_on_tick(false), has_on_spawn(false), has_on_load(false), has_on_tooltip(false), has_on_timer(false) {}
1432