1 /*
2 Copyright (C) 2009 - 2018 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 #include "scripting/lua_unit.hpp"
16
17 #include "formatter.hpp"
18 #include "game_board.hpp"
19 #include "log.hpp"
20 #include "map/location.hpp" // for map_location
21 #include "map/map.hpp"
22 #include "resources.hpp"
23 #include "scripting/lua_common.hpp"
24 #include "scripting/lua_unit_attacks.hpp"
25 #include "scripting/push_check.hpp"
26 #include "scripting/game_lua_kernel.hpp"
27 #include "units/unit.hpp"
28 #include "units/map.hpp"
29 #include "units/animation_component.hpp"
30
31 #include "lua/lauxlib.h"
32 #include "lua/lua.h" // for lua_State, lua_settop, etc
33
34 static lg::log_domain log_scripting_lua("scripting/lua");
35 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
36 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
37
38 static const char getunitKey[] = "unit";
39 static const char ustatusKey[] = "unit status";
40 static const char unitvarKey[] = "unit variables";
41
~lua_unit()42 lua_unit::~lua_unit()
43 {
44 }
45
get()46 unit* lua_unit::get()
47 {
48 if (ptr) return ptr.get();
49 if (c_ptr) return c_ptr;
50 if (side) {
51 return resources::gameboard->get_team(side).recall_list().find_if_matches_underlying_id(uid).get();
52 }
53 unit_map::unit_iterator ui = resources::gameboard->units().find(uid);
54 if (!ui.valid()) return nullptr;
55 return ui.get_shared_ptr().get(); //&*ui would not be legal, must get new shared_ptr by copy ctor because the unit_map itself is holding a boost shared pointer.
56 }
get_shared()57 unit_ptr lua_unit::get_shared()
58 {
59 if (ptr) return ptr;
60 if (side) {
61 return resources::gameboard->get_team(side).recall_list().find_if_matches_underlying_id(uid);
62 }
63 unit_map::unit_iterator ui = resources::gameboard->units().find(uid);
64 if (!ui.valid()) return unit_ptr();
65 return ui.get_shared_ptr(); //&*ui would not be legal, must get new shared_ptr by copy ctor because the unit_map itself is holding a boost shared pointer.
66 }
67
68 // Having this function here not only simplifies other code, it allows us to move
69 // pointers around from one structure to another.
70 // This makes bare pointer->map in particular about 2 orders of magnitude faster,
71 // as benchmarked from Lua code.
put_map(const map_location & loc)72 bool lua_unit::put_map(const map_location &loc)
73 {
74 if (ptr) {
75 unit_map::unit_iterator unit_it;
76 bool success = false;
77
78 std::tie(unit_it, success) = resources::gameboard->units().replace(loc, ptr);
79
80 if(success) {
81 ptr.reset();
82 uid = unit_it->underlying_id();
83 } else {
84 ERR_LUA << "Could not move unit " << ptr->underlying_id() << " onto map location " << loc << '\n';
85 return false;
86 }
87 } else if (side) { // recall list
88 unit_ptr it = resources::gameboard->get_team(side).recall_list().extract_if_matches_underlying_id(uid);
89 if (it) {
90 side = 0;
91 // uid may be changed by unit_map on insertion
92 uid = resources::gameboard->units().replace(loc, it).first->underlying_id();
93 } else {
94 ERR_LUA << "Could not find unit " << uid << " on recall list of side " << side << '\n';
95 return false;
96 }
97 } else { // on map
98 unit_map::unit_iterator ui = resources::gameboard->units().find(uid);
99 if (ui != resources::gameboard->units().end()) {
100 map_location from = ui->get_location();
101 if (from != loc) { // This check is redundant in current usage
102 resources::gameboard->units().erase(loc);
103 resources::gameboard->units().move(from, loc);
104 }
105 // No need to change our contents
106 } else {
107 ERR_LUA << "Could not find unit " << uid << " on the map" << std::endl;
108 return false;
109 }
110 }
111 return true;
112 }
113
luaW_isunit(lua_State * L,int index)114 bool luaW_isunit(lua_State* L, int index)
115 {
116 return luaL_testudata(L, index,getunitKey) != nullptr;
117 }
118
119 enum {
120 LU_OK,
121 LU_NOT_UNIT,
122 LU_NOT_ON_MAP,
123 LU_NOT_VALID,
124 };
125
internal_get_unit(lua_State * L,int index,bool only_on_map,int & error)126 static lua_unit* internal_get_unit(lua_State *L, int index, bool only_on_map, int& error)
127 {
128 error = LU_OK;
129 if(!luaW_isunit(L, index)) {
130 error = LU_NOT_UNIT;
131 return nullptr;
132 }
133 lua_unit* lu = static_cast<lua_unit*>(lua_touserdata(L, index));
134 if(only_on_map && !lu->on_map()) {
135 error = LU_NOT_ON_MAP;
136 }
137 if(!lu->get()) {
138 error = LU_NOT_VALID;
139 }
140 return lu;
141 }
142
luaW_tounit(lua_State * L,int index,bool only_on_map)143 unit* luaW_tounit(lua_State *L, int index, bool only_on_map)
144 {
145 int error;
146 lua_unit* lu = internal_get_unit(L, index, only_on_map, error);
147 if(error != LU_OK) {
148 return nullptr;
149 }
150 return lu->get();
151 }
152
luaW_tounit_ptr(lua_State * L,int index,bool only_on_map)153 unit_ptr luaW_tounit_ptr(lua_State *L, int index, bool only_on_map)
154 {
155 int error;
156 lua_unit* lu = internal_get_unit(L, index, only_on_map, error);
157 if(error != LU_OK) {
158 return nullptr;
159 }
160 return lu->get_shared();
161 }
162
luaW_tounit_ref(lua_State * L,int index)163 lua_unit* luaW_tounit_ref(lua_State *L, int index)
164 {
165 int error;
166 return internal_get_unit(L, index, false, error);
167 }
168
unit_show_error(lua_State * L,int index,int error)169 static void unit_show_error(lua_State *L, int index, int error)
170 {
171 switch(error) {
172 case LU_NOT_UNIT:
173 luaW_type_error(L, index, "unit");
174 break;
175 case LU_NOT_VALID:
176 luaL_argerror(L, index, "unit not found");
177 break;
178 case LU_NOT_ON_MAP:
179 luaL_argerror(L, index, "unit not found on map");
180 break;
181 }
182 }
183
luaW_checkunit_ptr(lua_State * L,int index,bool only_on_map)184 unit_ptr luaW_checkunit_ptr(lua_State *L, int index, bool only_on_map)
185 {
186 int error;
187 lua_unit* lu = internal_get_unit(L, index, only_on_map, error);
188 unit_show_error(L, index, error);
189 return lu->get_shared();
190 }
191
luaW_checkunit(lua_State * L,int index,bool only_on_map)192 unit& luaW_checkunit(lua_State *L, int index, bool only_on_map)
193 {
194 int error;
195 lua_unit* lu = internal_get_unit(L, index, only_on_map, error);
196 unit_show_error(L, index, error);
197 return *lu->get();
198 }
199
luaW_checkunit_ref(lua_State * L,int index)200 lua_unit* luaW_checkunit_ref(lua_State *L, int index)
201 {
202 int error;
203 lua_unit* lu = internal_get_unit(L, index, false, error);
204 unit_show_error(L, index, error);
205 return lu;
206 }
207
setmetatable(lua_State * L)208 void lua_unit::setmetatable(lua_State *L)
209 {
210 luaL_setmetatable(L, getunitKey);
211 }
212
luaW_pushlocalunit(lua_State * L,unit & u)213 lua_unit* luaW_pushlocalunit(lua_State *L, unit& u)
214 {
215 lua_unit* res = new(L) lua_unit(u);
216 lua_unit::setmetatable(L);
217 return res;
218 }
219
220 /**
221 * Destroys a unit object before it is collected (__gc metamethod).
222 */
impl_unit_collect(lua_State * L)223 static int impl_unit_collect(lua_State *L)
224 {
225 lua_unit *u = static_cast<lua_unit *>(lua_touserdata(L, 1));
226 u->lua_unit::~lua_unit();
227 return 0;
228 }
229
230 /**
231 * Checks two lua proxy units for equality. (__eq metamethod)
232 */
impl_unit_equality(lua_State * L)233 static int impl_unit_equality(lua_State* L)
234 {
235 unit& left = luaW_checkunit(L, 1);
236 unit& right = luaW_checkunit(L, 2);
237 const bool equal = left.underlying_id() == right.underlying_id();
238 lua_pushboolean(L, equal);
239 return 1;
240 }
241
242 /**
243 * Gets some data on a unit (__index metamethod).
244 * - Arg 1: full userdata containing the unit id.
245 * - Arg 2: string containing the name of the property.
246 * - Ret 1: something containing the attribute.
247 */
impl_unit_get(lua_State * L)248 static int impl_unit_get(lua_State *L)
249 {
250 lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
251 char const *m = luaL_checkstring(L, 2);
252 const unit* pu = lu->get();
253
254 if(strcmp(m, "valid") == 0) {
255 if(!pu) {
256 return 0;
257 }
258 if(lu->on_map()) {
259 lua_pushstring(L, "map");
260 } else if(lu->on_recall_list()) {
261 lua_pushstring(L, "recall");
262 } else {
263 lua_pushstring(L, "private");
264 }
265 return 1;
266 }
267
268 if(!pu) {
269 return luaL_argerror(L, 1, "unknown unit");
270 }
271
272 const unit& u = *pu;
273
274 // Find the corresponding attribute.
275 return_int_attrib("x", u.get_location().wml_x());
276 return_int_attrib("y", u.get_location().wml_y());
277 if(strcmp(m, "loc") == 0) {
278 lua_pushinteger(L, u.get_location().wml_x());
279 lua_pushinteger(L, u.get_location().wml_y());
280 return 2;
281 }
282 return_int_attrib("side", u.side());
283 return_string_attrib("id", u.id());
284 return_string_attrib("type", u.type_id());
285 return_string_attrib("image_mods", u.effect_image_mods());
286 return_string_attrib("usage", u.usage());
287 return_int_attrib("hitpoints", u.hitpoints());
288 return_int_attrib("max_hitpoints", u.max_hitpoints());
289 return_int_attrib("experience", u.experience());
290 return_int_attrib("max_experience", u.max_experience());
291 return_int_attrib("recall_cost", u.recall_cost());
292 return_int_attrib("moves", u.movement_left());
293 return_int_attrib("max_moves", u.total_movement());
294 return_int_attrib("max_attacks", u.max_attacks());
295 return_int_attrib("attacks_left", u.attacks_left());
296 return_tstring_attrib("name", u.name());
297 return_bool_attrib("canrecruit", u.can_recruit());
298 return_int_attrib("level", u.level());
299 return_int_attrib("cost", u.cost());
300
301 return_vector_string_attrib("extra_recruit", u.recruits());
302 return_vector_string_attrib("advances_to", u.advances_to());
303
304 if(strcmp(m, "alignment") == 0) {
305 lua_push(L, u.alignment());
306 return 1;
307 }
308
309 if(strcmp(m, "upkeep") == 0) {
310 unit::upkeep_t upkeep = u.upkeep_raw();
311
312 // Need to keep these separate in order to ensure an int value is always used if applicable.
313 if(int* v = boost::get<int>(&upkeep)) {
314 lua_push(L, *v);
315 } else {
316 const std::string type = boost::apply_visitor(unit::upkeep_type_visitor(), upkeep);
317 lua_push(L, type);
318 }
319
320 return 1;
321 }
322 if(strcmp(m, "advancements") == 0) {
323 lua_push(L, u.modification_advancements());
324 return 1;
325 }
326 if(strcmp(m, "overlays") == 0) {
327 lua_push(L, u.overlays());
328 return 1;
329 }
330 if(strcmp(m, "traits") == 0) {
331 lua_push(L, u.get_traits_list());
332 return 1;
333 }
334 if(strcmp(m, "abilities") == 0) {
335 lua_push(L, u.get_ability_list());
336 return 1;
337 }
338 if(strcmp(m, "status") == 0) {
339 lua_createtable(L, 1, 0);
340 lua_pushvalue(L, 1);
341 lua_rawseti(L, -2, 1);
342 luaL_setmetatable(L, ustatusKey);
343 return 1;
344 }
345 if(strcmp(m, "variables") == 0) {
346 lua_createtable(L, 1, 0);
347 lua_pushvalue(L, 1);
348 lua_rawseti(L, -2, 1);
349 luaL_setmetatable(L, unitvarKey);
350 return 1;
351 }
352 if(strcmp(m, "attacks") == 0) {
353 push_unit_attacks_table(L, 1);
354 return 1;
355 }
356 return_vector_string_attrib("animations", u.anim_comp().get_flags());
357 return_cfg_attrib("recall_filter", cfg = u.recall_filter());
358 return_bool_attrib("hidden", u.get_hidden());
359 return_bool_attrib("petrified", u.incapacitated());
360 return_bool_attrib("resting", u.resting());
361 return_string_attrib("role", u.get_role());
362 return_string_attrib("race", u.race()->id());
363 return_string_attrib("gender", gender_string(u.gender()));
364 return_string_attrib("variation", u.variation());
365 return_bool_attrib("zoc", u.get_emit_zoc());
366 return_string_attrib("facing", map_location::write_direction(u.facing()));
367 return_string_attrib("portrait", u.big_profile() == u.absolute_image() ? u.absolute_image() + u.image_mods() : u.big_profile());
368 return_cfg_attrib("__cfg", u.write(cfg); u.get_location().write(cfg));
369
370 if(luaW_getmetafield(L, 1, m)) {
371 return 1;
372 }
373 return 0;
374 }
375
376 /**
377 * Sets some data on a unit (__newindex metamethod).
378 * - Arg 1: full userdata containing the unit id.
379 * - Arg 2: string containing the name of the property.
380 * - Arg 3: something containing the attribute.
381 */
impl_unit_set(lua_State * L)382 static int impl_unit_set(lua_State *L)
383 {
384 lua_unit *lu = static_cast<lua_unit *>(lua_touserdata(L, 1));
385 char const *m = luaL_checkstring(L, 2);
386 unit* pu = lu->get();
387 if (!pu) return luaL_argerror(L, 1, "unknown unit");
388 unit &u = *pu;
389
390 // Find the corresponding attribute.
391 //modify_int_attrib_check_range("side", u.set_side(value), 1, static_cast<int>(teams().size())); TODO: Figure out if this is a good idea, to refer to teams() and make this depend on having a gamestate
392 modify_int_attrib("side", u.set_side(value));
393 modify_int_attrib("moves", u.set_movement(value));
394 modify_int_attrib("hitpoints", u.set_hitpoints(value));
395 modify_int_attrib("experience", u.set_experience(value));
396 modify_int_attrib("recall_cost", u.set_recall_cost(value));
397 modify_int_attrib("attacks_left", u.set_attacks(value));
398 modify_int_attrib("level", u.set_level(value));
399 modify_bool_attrib("resting", u.set_resting(value));
400 modify_tstring_attrib("name", u.set_name(value));
401 modify_string_attrib("role", u.set_role(value));
402 modify_string_attrib("facing", u.set_facing(map_location::parse_direction(value)));
403 modify_bool_attrib("hidden", u.set_hidden(value));
404 modify_bool_attrib("zoc", u.set_emit_zoc(value));
405 modify_bool_attrib("canrecruit", u.set_can_recruit(value));
406
407 modify_vector_string_attrib("extra_recruit", u.set_recruits(value));
408 modify_vector_string_attrib("advances_to", u.set_advances_to(value));
409 if(strcmp(m, "alignment") == 0) {
410 u.set_alignment(lua_check<unit_type::ALIGNMENT>(L, 3));
411 return 0;
412 }
413
414 if(strcmp(m, "advancements") == 0) {
415 u.set_advancements(lua_check<std::vector<config>>(L, 3));
416 return 0;
417 }
418
419 if(strcmp(m, "upkeep") == 0) {
420 if(lua_isnumber(L, 3)) {
421 u.set_upkeep(luaL_checkinteger(L, 3));
422 return 0;
423 }
424 const char* v = luaL_checkstring(L, 3);
425 if((strcmp(v, "loyal") == 0) || (strcmp(v, "free") == 0)) {
426 u.set_upkeep(unit::upkeep_loyal());
427 } else if(strcmp(v, "full") == 0) {
428 u.set_upkeep(unit::upkeep_full());
429 } else {
430 std::string err_msg = "unknown upkeep value of unit: ";
431 err_msg += v;
432 return luaL_argerror(L, 2, err_msg.c_str());
433 }
434 return 0;
435 }
436
437 if(!lu->on_map()) {
438 map_location loc = u.get_location();
439 modify_int_attrib("x", loc.set_wml_x(value); u.set_location(loc));
440 modify_int_attrib("y", loc.set_wml_y(value); u.set_location(loc));
441 modify_string_attrib("id", u.set_id(value));
442 } else {
443 const bool is_key_x = strcmp(m, "x") == 0;
444 const bool is_key_y = strcmp(m, "y") == 0;
445 const bool is_loc_key = strcmp(m, "loc") == 0;
446
447 // Handle moving an on-map unit
448 if(is_key_x || is_key_y || is_loc_key) {
449 game_board* gb = resources::gameboard;
450
451 if(!gb) {
452 return 0;
453 }
454
455 map_location src = u.get_location();
456 map_location dst = src;
457
458 if(is_key_x) {
459 dst.set_wml_x(luaL_checkinteger(L, 3));
460 } else if(is_key_y) {
461 dst.set_wml_y(luaL_checkinteger(L, 3));
462 } else {
463 dst = luaW_checklocation(L, 3);
464 }
465
466 // TODO: could probably be relegated to a helper function.
467 if(src != dst) {
468 // If the dst isn't on the map, the unit will be clobbered. Guard against that.
469 if(!gb->map().on_board(dst)) {
470 std::string err_msg = formatter() << "destination hex not on map (excluding border): " << dst;
471 return luaL_argerror(L, 2, err_msg.c_str());
472 }
473
474 unit_map::iterator unit_iterator = gb->units().end();
475 bool success = false;
476
477 std::tie(unit_iterator, success) = gb->units().move(src, dst);
478
479 if(success) {
480 unit_iterator->anim_comp().set_standing();
481 }
482 }
483
484 return 0;
485 }
486 }
487
488 std::string err_msg = "unknown modifiable property of unit: ";
489 err_msg += m;
490 return luaL_argerror(L, 2, err_msg.c_str());
491 }
492
493 /**
494 * Gets the status of a unit (__index metamethod).
495 * - Arg 1: table containing the userdata containing the unit id.
496 * - Arg 2: string containing the name of the status.
497 * - Ret 1: boolean.
498 */
impl_unit_status_get(lua_State * L)499 static int impl_unit_status_get(lua_State *L)
500 {
501 if(!lua_istable(L, 1)) {
502 return luaW_type_error(L, 1, "unit status");
503 }
504 lua_rawgeti(L, 1, 1);
505 const unit* u = luaW_tounit(L, -1);
506 if(!u) {
507 return luaL_argerror(L, 1, "unknown unit");
508 }
509 char const *m = luaL_checkstring(L, 2);
510 lua_pushboolean(L, u->get_state(m));
511 return 1;
512 }
513
514 /**
515 * Sets the status of a unit (__newindex metamethod).
516 * - Arg 1: table containing the userdata containing the unit id.
517 * - Arg 2: string containing the name of the status.
518 * - Arg 3: boolean.
519 */
impl_unit_status_set(lua_State * L)520 static int impl_unit_status_set(lua_State *L)
521 {
522 if(!lua_istable(L, 1)) {
523 return luaW_type_error(L, 1, "unit status");
524 }
525 lua_rawgeti(L, 1, 1);
526 unit* u = luaW_tounit(L, -1);
527 if(!u) {
528 return luaL_argerror(L, 1, "unknown unit");
529 }
530 char const *m = luaL_checkstring(L, 2);
531 u->set_state(m, luaW_toboolean(L, 3));
532 return 0;
533 }
534
535 /**
536 * Gets the variable of a unit (__index metamethod).
537 * - Arg 1: table containing the userdata containing the unit id.
538 * - Arg 2: string containing the name of the status.
539 * - Ret 1: boolean.
540 */
impl_unit_variables_get(lua_State * L)541 static int impl_unit_variables_get(lua_State *L)
542 {
543 if(!lua_istable(L, 1)) {
544 return luaW_type_error(L, 1, "unit variables");
545 }
546 lua_rawgeti(L, 1, 1);
547 const unit* u = luaW_tounit(L, -1);
548 if(!u) {
549 return luaL_argerror(L, 2, "unknown unit");
550 }
551 char const *m = luaL_checkstring(L, 2);
552 return_cfgref_attrib("__cfg", u->variables());
553
554 variable_access_const v(m, u->variables());
555 return luaW_pushvariable(L, v) ? 1 : 0;
556 }
557
558 /**
559 * Sets the variable of a unit (__newindex metamethod).
560 * - Arg 1: table containing the userdata containing the unit id.
561 * - Arg 2: string containing the name of the status.
562 * - Arg 3: scalar.
563 */
impl_unit_variables_set(lua_State * L)564 static int impl_unit_variables_set(lua_State *L)
565 {
566 if(!lua_istable(L, 1)) {
567 return luaW_type_error(L, 1, "unit variables");
568 }
569 lua_rawgeti(L, 1, 1);
570 unit* u = luaW_tounit(L, -1);
571 if(!u) {
572 return luaL_argerror(L, 2, "unknown unit");
573 }
574 char const *m = luaL_checkstring(L, 2);
575 if(strcmp(m, "__cfg") == 0) {
576 u->variables() = luaW_checkconfig(L, 3);
577 return 0;
578 }
579 config& vars = u->variables();
580 if(lua_isnoneornil(L, 3)) {
581 try {
582 variable_access_throw(m, vars).clear(false);
583 } catch(const invalid_variablename_exception&) {
584 }
585 return 0;
586 }
587 variable_access_create v(m, vars);
588 luaW_checkvariable(L, v, 3);
589 return 0;
590 }
591
592 namespace lua_units {
register_metatables(lua_State * L)593 std::string register_metatables(lua_State* L)
594 {
595 std::ostringstream cmd_out;
596
597 // Create the getunit metatable.
598 cmd_out << "Adding getunit metatable...\n";
599
600 luaL_newmetatable(L, getunitKey);
601 lua_pushcfunction(L, impl_unit_collect);
602 lua_setfield(L, -2, "__gc");
603 lua_pushcfunction(L, impl_unit_equality);
604 lua_setfield(L, -2, "__eq");
605 lua_pushcfunction(L, impl_unit_get);
606 lua_setfield(L, -2, "__index");
607 lua_pushcfunction(L, impl_unit_set);
608 lua_setfield(L, -2, "__newindex");
609 lua_pushstring(L, "unit");
610 lua_setfield(L, -2, "__metatable");
611 // Unit methods
612 luaW_getglobal(L, "wesnoth", "match_unit");
613 lua_setfield(L, -2, "matches");
614 luaW_getglobal(L, "wesnoth", "put_recall_unit");
615 lua_setfield(L, -2, "to_recall");
616 luaW_getglobal(L, "wesnoth", "put_unit");
617 lua_setfield(L, -2, "to_map");
618 luaW_getglobal(L, "wesnoth", "erase_unit");
619 lua_setfield(L, -2, "erase");
620 luaW_getglobal(L, "wesnoth", "copy_unit");
621 lua_setfield(L, -2, "clone");
622 luaW_getglobal(L, "wesnoth", "extract_unit");
623 lua_setfield(L, -2, "extract");
624 luaW_getglobal(L, "wesnoth", "advance_unit");
625 lua_setfield(L, -2, "advance");
626 luaW_getglobal(L, "wesnoth", "add_modification");
627 lua_setfield(L, -2, "add_modification");
628 luaW_getglobal(L, "wesnoth", "unit_resistance");
629 lua_setfield(L, -2, "resistance");
630 luaW_getglobal(L, "wesnoth", "remove_modifications");
631 lua_setfield(L, -2, "remove_modifications");
632 luaW_getglobal(L, "wesnoth", "unit_defense");
633 lua_setfield(L, -2, "defense");
634 luaW_getglobal(L, "wesnoth", "unit_movement_cost");
635 lua_setfield(L, -2, "movement");
636 luaW_getglobal(L, "wesnoth", "unit_vision_cost");
637 lua_setfield(L, -2, "vision");
638 luaW_getglobal(L, "wesnoth", "unit_jamming_cost");
639 lua_setfield(L, -2, "jamming");
640 luaW_getglobal(L, "wesnoth", "unit_ability");
641 lua_setfield(L, -2, "ability");
642 luaW_getglobal(L, "wesnoth", "transform_unit");
643 lua_setfield(L, -2, "transform");
644 luaW_getglobal(L, "wesnoth", "select_unit");
645 lua_setfield(L, -2, "select");
646
647 // Create the unit status metatable.
648 cmd_out << "Adding unit status metatable...\n";
649
650 luaL_newmetatable(L, ustatusKey);
651 lua_pushcfunction(L, impl_unit_status_get);
652 lua_setfield(L, -2, "__index");
653 lua_pushcfunction(L, impl_unit_status_set);
654 lua_setfield(L, -2, "__newindex");
655 lua_pushstring(L, "unit status");
656 lua_setfield(L, -2, "__metatable");
657
658 // Create the unit variables metatable.
659 cmd_out << "Adding unit variables metatable...\n";
660
661 luaL_newmetatable(L, unitvarKey);
662 lua_pushcfunction(L, impl_unit_variables_get);
663 lua_setfield(L, -2, "__index");
664 lua_pushcfunction(L, impl_unit_variables_set);
665 lua_setfield(L, -2, "__newindex");
666 lua_pushstring(L, "unit variables");
667 lua_setfield(L, -2, "__metatable");
668
669 return cmd_out.str();
670 }
671 }
672