The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lua_common.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 - 2025
3 by Chris Beck <render787@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * @file
18 * Contains code common to the application and game lua kernels which
19 * cannot or should not go into the lua kernel base files.
20 *
21 * Currently contains implementation functions related to vconfig and
22 * gettext, also some macros to assist in writing C lua callbacks.
23 */
24
26
27#include "config.hpp"
29#include "tstring.hpp" // for t_string
30#include "variable.hpp" // for vconfig
31#include "log.hpp"
32#include "gettext.hpp"
34#include "game_display.hpp"
35
36#include <cstring>
37#include <iterator> // for distance, advance
38#include <string> // for string, basic_string
39
40
41static const char gettextKey[] = "gettext";
42static const char vconfigKey[] = "vconfig";
43static const char vconfigpairsKey[] = "vconfig pairs";
44static const char tstringKey[] = "translatable string";
45static const char executeKey[] = "err";
46
47static lg::log_domain log_scripting_lua("scripting/lua");
48#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
49#define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
50#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
51
52static lg::log_domain log_wml("wml");
53#define ERR_WML LOG_STREAM(err, log_wml)
54
55namespace lua_common {
56
57/**
58 * Creates a t_string object (__call metamethod).
59 * - Arg 1: userdata containing the domain.
60 * - Arg 2: string to translate.
61 * - Ret 1: string containing the translatable string.
62 */
63static int impl_gettext(lua_State *L)
64{
65 char const *m = luaL_checkstring(L, 2);
66 char const *d = static_cast<char *>(luaL_checkudata(L, 1, gettextKey));
67 // Hidden metamethod, so d has to be a string. Use it to create a t_string.
68 if(lua_isstring(L, 3)) {
69 const char* pl = luaL_checkstring(L, 3);
70 int count = luaL_checkinteger(L, 4);
71 luaW_pushtstring(L, t_string(m, pl, count, d));
72 } else {
74 }
75 return 1;
76}
77
78static int impl_gettext_tostr(lua_State* L)
79{
80 char* d = static_cast<char*>(luaL_checkudata(L, 1, gettextKey));
81 using namespace std::literals;
82 std::string str = "textdomain: "s + d;
83 lua_push(L, str);
84 return 1;
85}
86
87/**
88 * Creates an interface for gettext
89 * - Arg 1: string containing the domain.
90 * - Ret 1: a full userdata with __call pointing to lua_gettext.
91 */
92int intf_textdomain(lua_State *L)
93{
94 std::size_t l;
95 char const *m = luaL_checklstring(L, 1, &l);
96
97 void *p = lua_newuserdatauv(L, l + 1, 0);
98 memcpy(p, m, l + 1);
99
100 luaL_setmetatable(L, gettextKey);
101 return 1;
102}
103
104/**
105 * Converts a Lua value at position @a src and appends it to @a dst.
106 * @note This function is private to lua_tstring_concat. It expects two things.
107 * First, the t_string metatable is at the top of the stack on entry. (It
108 * is still there on exit.) Second, the caller hasn't any valuable object
109 * with dynamic lifetime, since they would be leaked on error.
110 */
111static void tstring_concat_aux(lua_State *L, t_string &dst, int src)
112{
113 switch (lua_type(L, src)) {
114 case LUA_TNUMBER:
115 case LUA_TSTRING:
116 dst += lua_tostring(L, src);
117 return;
118 case LUA_TUSERDATA:
119 // Compare its metatable with t_string's metatable.
120 if (t_string * src_ptr = static_cast<t_string *> (luaL_testudata(L, src, tstringKey))) {
121 dst += *src_ptr;
122 return;
123 }
124 //intentional fall-through
125 default:
126 luaW_type_error(L, src, "string");
127 }
128}
129
130/**
131 * Appends a scalar to a t_string object (__concat metamethod).
132 */
133static int impl_tstring_concat(lua_State *L)
134{
135 // Create a new t_string.
136 t_string *t = new(L) t_string;
137 luaL_setmetatable(L, tstringKey);
138
139 // Append both arguments to t.
140 tstring_concat_aux(L, *t, 1);
141 tstring_concat_aux(L, *t, 2);
142
143 return 1;
144}
145
146static int impl_tstring_len(lua_State* L)
147{
148 t_string* t = static_cast<t_string*>(luaL_checkudata(L, 1, tstringKey));
149 lua_pushnumber(L, t->size());
150 return 1;
151}
152
153/**
154 * Destroys a t_string object before it is collected (__gc metamethod).
155 */
156static int impl_tstring_collect(lua_State *L)
157{
158 t_string *t = static_cast<t_string *>(luaL_checkudata(L, 1, tstringKey));
159 t->t_string::~t_string();
160 return 0;
161}
162
163static int impl_tstring_lt(lua_State *L)
164{
165 t_string *t1 = static_cast<t_string *>(luaL_checkudata(L, 1, tstringKey));
166 t_string *t2 = static_cast<t_string *>(luaL_checkudata(L, 2, tstringKey));
167 lua_pushboolean(L, translation::compare(t1->get(), t2->get()) < 0);
168 return 1;
169}
170
171static int impl_tstring_le(lua_State *L)
172{
173 t_string *t1 = static_cast<t_string *>(luaL_checkudata(L, 1, tstringKey));
174 t_string *t2 = static_cast<t_string *>(luaL_checkudata(L, 2, tstringKey));
175 lua_pushboolean(L, translation::compare(t1->get(), t2->get()) < 1);
176 return 1;
177}
178
179static int impl_tstring_eq(lua_State *L)
180{
181 t_string *t1 = static_cast<t_string *>(luaL_checkudata(L, 1, tstringKey));
182 t_string *t2 = static_cast<t_string *>(luaL_checkudata(L, 2, tstringKey));
183 lua_pushboolean(L, translation::compare(t1->get(), t2->get()) == 0);
184 return 1;
185}
186
187/**
188 * Converts a t_string object to a string (__tostring metamethod);
189 * that is, performs a translation.
190 */
191static int impl_tstring_tostring(lua_State *L)
192{
193 t_string *t = static_cast<t_string *>(luaL_checkudata(L, 1, tstringKey));
194 lua_pushstring(L, t->c_str());
195 return 1;
196}
197
198/**
199 * Gets the parsed field of a vconfig object (_index metamethod).
200 * Special fields __literal, __shallow_literal, __parsed, and
201 * __shallow_parsed, return Lua tables.
202 */
203static int impl_vconfig_get(lua_State *L)
204{
205 static const lua_named_tuple_builder tuple_builder{ {"tag", "contents"} };
206
207 vconfig *v = static_cast<vconfig *>(luaL_checkudata(L, 1, vconfigKey));
208
209 if (lua_isnumber(L, 2))
210 {
212 unsigned len = std::distance(i, v->ordered_end());
213 unsigned pos = lua_tointeger(L, 2) - 1;
214 if (pos >= len) return 0;
215 std::advance(i, pos);
216
217 tuple_builder.push(L);
218 lua_pushstring(L, i.get_key().c_str());
219 lua_rawseti(L, -2, 1);
220 luaW_pushvconfig(L, i.get_child());
221 lua_rawseti(L, -2, 2);
222 return 1;
223 }
224
225 char const *m = luaL_checkstring(L, 2);
226 if (strcmp(m, "__literal") == 0) {
228 return 1;
229 }
230 if (strcmp(m, "__parsed") == 0) {
232 return 1;
233 }
234
235 bool shallow_literal = strcmp(m, "__shallow_literal") == 0;
236 if (shallow_literal || strcmp(m, "__shallow_parsed") == 0)
237 {
238 lua_newtable(L);
239 for(const auto& [key, value] : v->get_config().attribute_range()) {
240 if (shallow_literal)
242 else
243 luaW_pushscalar(L, v->expand(key));
244 lua_setfield(L, -2, key.c_str());
245 }
247 i_end = v->ordered_end();
248 if (shallow_literal) {
250 i_end.disable_insertion();
251 }
252 for (int j = 1; i != i_end; ++i, ++j)
253 {
254 tuple_builder.push(L);
255 lua_pushlstring(L, i.get_key().c_str(), i.get_key().size());
256 lua_rawseti(L, -2, 1);
257 luaW_pushvconfig(L, i.get_child());
258 lua_rawseti(L, -2, 2);
259 lua_rawseti(L, -2, j);
260 }
261 return 1;
262 }
263
264 if (v->null() || !v->has_attribute(m)) return 0;
265 luaW_pushscalar(L, (*v)[m]);
266 return 1;
267}
268
269static int impl_vconfig_dir(lua_State* L)
270{
271 vconfig *v = static_cast<vconfig *>(luaL_checkudata(L, 1, vconfigKey));
272 std::vector<std::string> attributes;
273 for(const auto& [key, value] : v->get_config().attribute_range()) {
274 attributes.push_back(key);
275 }
276 lua_push(L, attributes);
277 return 1;
278}
279
280/**
281 * Returns the number of a child of a vconfig object.
282 */
283static int impl_vconfig_size(lua_State *L)
284{
285 vconfig *v = static_cast<vconfig *>(luaL_checkudata(L, 1, vconfigKey));
286 lua_pushinteger(L, v->null() ? 0 :
287 std::distance(v->ordered_begin(), v->ordered_end()));
288 return 1;
289}
290
291/**
292 * Destroys a vconfig object before it is collected (__gc metamethod).
293 */
294static int impl_vconfig_collect(lua_State *L)
295{
296 vconfig *v = static_cast<vconfig *>(luaL_checkudata(L, 1, vconfigKey));
297 v->~vconfig();
298 return 0;
299}
300
301/**
302 * Iterate through the attributes of a vconfig
303 */
304static int impl_vconfig_pairs_iter(lua_State *L)
305{
306 vconfig vcfg = luaW_checkvconfig(L, 1);
307 void* p = luaL_checkudata(L, lua_upvalueindex(1), vconfigpairsKey);
308 config::const_attr_itors& range = *static_cast<config::const_attr_itors*>(p);
309 if (range.empty()) {
310 return 0;
311 }
312 config::attribute value = range.front();
313 range.pop_front();
314 lua_pushlstring(L, value.first.c_str(), value.first.length());
315 luaW_pushscalar(L, vcfg[value.first]);
316 return 2;
317}
318
319/**
320 * Destroy a vconfig pairs iterator
321 */
322static int impl_vconfig_pairs_collect(lua_State *L)
323{
324 typedef config::const_attr_itors const_attr_itors;
325 void* p = luaL_checkudata(L, 1, vconfigpairsKey);
326
327 // Triggers a false positive of C4189 with Visual Studio. Suppress.
328#if defined(_MSC_VER)
329#pragma warning(push)
330#pragma warning(disable: 4189)
331#endif
332
333 const_attr_itors* cai = static_cast<const_attr_itors*>(p);
334 cai->~const_attr_itors();
335
336#if defined(_MSC_VER)
337#pragma warning(pop)
338#endif
339
340 return 0;
341}
342
343/**
344 * Construct an iterator to iterate through the attributes of a vconfig
345 */
346static int impl_vconfig_pairs(lua_State *L)
347{
348 vconfig vcfg = luaW_checkvconfig(L, 1);
350 luaL_newmetatable(L, vconfigpairsKey);
351 lua_setmetatable(L, -2);
352 lua_pushcclosure(L, &impl_vconfig_pairs_iter, 1);
353 lua_pushvalue(L, 1);
354 return 2;
355}
356
357/**
358 * Creates a vconfig containing the WML table.
359 * - Arg 1: WML table.
360 * - Ret 1: vconfig userdata.
361 */
362int intf_tovconfig(lua_State *L)
363{
364 vconfig vcfg = luaW_checkvconfig(L, 1);
365 luaW_pushvconfig(L, vcfg);
366 return 1;
367}
368
369/**
370 * Adds the gettext metatable
371 */
372std::string register_gettext_metatable(lua_State *L)
373{
374 luaL_newmetatable(L, gettextKey);
375
376 static luaL_Reg const callbacks[] {
377 { "__call", &impl_gettext},
378 { "__tostring", &impl_gettext_tostr},
379 { nullptr, nullptr }
380 };
381 luaL_setfuncs(L, callbacks, 0);
382
383 lua_pushstring(L, "message domain");
384 lua_setfield(L, -2, "__metatable");
385
386 return "Adding gettext metatable...\n";
387}
388
389/**
390 * Adds the tstring metatable
391 */
392std::string register_tstring_metatable(lua_State *L)
393{
394 luaL_newmetatable(L, tstringKey);
395
396 static luaL_Reg const callbacks[] {
397 { "__concat", &impl_tstring_concat},
398 { "__gc", &impl_tstring_collect},
399 { "__tostring", &impl_tstring_tostring},
400 { "__len", &impl_tstring_len},
401 { "__lt", &impl_tstring_lt},
402 { "__le", &impl_tstring_le},
403 { "__eq", &impl_tstring_eq},
404 { nullptr, nullptr }
405 };
406 luaL_setfuncs(L, callbacks, 0);
407
408 lua_createtable(L, 0, 1);
409 luaW_getglobal(L, "string", "format");
410 lua_setfield(L, -2, "format");
411 luaW_getglobal(L, "stringx", "vformat");
412 lua_setfield(L, -2, "vformat");
413 lua_setfield(L, -2, "__index");
414
415 lua_pushstring(L, "translatable string");
416 lua_setfield(L, -2, "__metatable");
417
418 return "Adding tstring metatable...\n";
419}
420
421/**
422 * Adds the vconfig metatable
423 */
424std::string register_vconfig_metatable(lua_State *L)
425{
426 luaL_newmetatable(L, vconfigKey);
427
428 static luaL_Reg const callbacks[] {
429 { "__gc", &impl_vconfig_collect},
430 { "__index", &impl_vconfig_get},
431 { "__dir", &impl_vconfig_dir},
432 { "__len", &impl_vconfig_size},
433 { "__pairs", &impl_vconfig_pairs},
434 { nullptr, nullptr }
435 };
436 luaL_setfuncs(L, callbacks, 0);
437
438 lua_pushstring(L, "wml object");
439 lua_setfield(L, -2, "__metatable");
440
441 // Metatables for the iterator userdata
442
443 // I don't bother setting __metatable because this
444 // userdata is only ever stored in the iterator's
445 // upvalues, so it's never visible to the user.
446 luaL_newmetatable(L, vconfigpairsKey);
447 lua_pushstring(L, "__gc");
448 lua_pushcfunction(L, &impl_vconfig_pairs_collect);
449 lua_rawset(L, -3);
450
451 return "Adding vconfig metatable...\n";
452}
453
454} // end namespace lua_common
455
457 : state_(L)
458{
459 lua_geti(state_, -1, arg_index);
460}
461
462scoped_lua_argument::scoped_lua_argument(lua_State* L, int value_index, int arg_index)
463 : state_(L)
464{
465 lua_geti(state_, value_index, arg_index);
466}
467
472
473void* operator new(std::size_t sz, lua_State *L, int nuv)
474{
475 return lua_newuserdatauv(L, sz, nuv);
476}
477
478void operator delete(void*, lua_State *L, int)
479{
480 // Not sure if this is needed since it's a no-op
481 // It's only called if a constructor throws while using the above operator new
482 // By removing the userdata from the stack, this should ensure that Lua frees it
483 lua_pop(L, 1);
484}
485
486bool luaW_getmetafield(lua_State *L, int idx, const char* key)
487{
488 if(key == nullptr) {
489 return false;
490 }
491 int n = strlen(key);
492 if(n == 0) {
493 return false;
494 }
495 if(n >= 2 && key[0] == '_' && key[1] == '_') {
496 return false;
497 }
498 return luaL_getmetafield(L, idx, key) != 0;
499}
500
501void luaW_pushvconfig(lua_State *L, const vconfig& cfg)
502{
503 new(L) vconfig(cfg);
504 luaL_setmetatable(L, vconfigKey);
505}
506
507void luaW_pushtstring(lua_State *L, const t_string& v)
508{
509 new(L) t_string(v);
510 luaL_setmetatable(L, tstringKey);
511}
512
513
514namespace {
515 struct luaW_pushscalar_visitor
516#ifdef USING_BOOST_VARIANT
517 : boost::static_visitor<>
518#endif
519 {
520 lua_State *L;
521 luaW_pushscalar_visitor(lua_State *l): L(l) {}
522
523 void operator()(const utils::monostate&) const
524 { lua_pushnil(L); }
525 void operator()(bool b) const
526 { lua_pushboolean(L, b); }
527 void operator()(int i) const
528 { lua_pushinteger(L, i); }
529 void operator()(unsigned long long ull) const
530 { lua_pushnumber(L, ull); }
531 void operator()(double d) const
532 { lua_pushnumber(L, d); }
533 void operator()(const std::string& s) const
534 { lua_pushlstring(L, s.c_str(), s.size()); }
535 void operator()(const t_string& s) const
536 { luaW_pushtstring(L, s); }
537 };
538}//unnamed namespace for luaW_pushscalar_visitor
539
540void luaW_pushscalar(lua_State *L, const config::attribute_value& v)
541{
542 v.apply_visitor(luaW_pushscalar_visitor(L));
543}
544
545bool luaW_toscalar(lua_State *L, int index, config::attribute_value& v)
546{
547 switch (lua_type(L, index)) {
548 case LUA_TBOOLEAN:
549 v = luaW_toboolean(L, -1);
550 break;
551 case LUA_TNUMBER:
552 v = lua_tonumber(L, -1);
553 break;
554 case LUA_TSTRING:
555 v = std::string(luaW_tostring(L, -1));
556 break;
557 case LUA_TUSERDATA:
558 {
559 if (t_string * tptr = static_cast<t_string *>(luaL_testudata(L, -1, tstringKey))) {
560 v = *tptr;
561 break;
562 } else {
563 return false;
564 }
565 }
566 default:
567 return false;
568 }
569 return true;
570}
571
572bool luaW_totstring(lua_State *L, int index, t_string &str)
573{
574 switch (lua_type(L, index)) {
575 case LUA_TBOOLEAN:
576 str = lua_toboolean(L, index) ? "yes" : "no";
577 break;
578 case LUA_TNUMBER:
579 case LUA_TSTRING:
580 str = lua_tostring(L, index);
581 break;
582 case LUA_TUSERDATA:
583 {
584 if (t_string * tstr = static_cast<t_string *> (luaL_testudata(L, index, tstringKey))) {
585 str = *tstr;
586 break;
587 } else {
588 return false;
589 }
590 }
591 default:
592 return false;
593 }
594 return true;
595}
596
597t_string luaW_checktstring(lua_State *L, int index)
598{
599 t_string result;
600 if (!luaW_totstring(L, index, result))
601 luaW_type_error(L, index, "translatable string");
602 return result;
603}
604
605bool luaW_iststring(lua_State* L, int index)
606{
607 if(lua_isstring(L, index)) {
608 return true;
609 }
610 if(lua_isuserdata(L, index) && luaL_testudata(L, index, tstringKey)) {
611 return true;
612 }
613 return false;
614}
615
616void luaW_filltable(lua_State *L, const config& cfg)
617{
618 static const lua_named_tuple_builder tuple_builder{ {"tag", "contents"} };
619
620 if (!lua_checkstack(L, LUA_MINSTACK))
621 return;
622
623 int k = 1;
624 for(const auto [child_key, child_cfg] : cfg.all_children_view())
625 {
626 tuple_builder.push(L);
627 lua_pushstring(L, child_key.c_str());
628 lua_rawseti(L, -2, 1);
629 lua_newtable(L);
630 luaW_filltable(L, child_cfg);
631 lua_rawseti(L, -2, 2);
632 lua_rawseti(L, -2, k++);
633 }
634 for(const auto& [key, value] : cfg.attribute_range())
635 {
637 lua_setfield(L, -2, key.c_str());
638 }
639}
640
642{
643 if(lua_type(L, 2) == LUA_TSTRING) {
644 std::string k = lua_tostring(L, 2);
645 luaL_getmetafield(L, 1, "__names");
646 auto names = lua_check<std::vector<std::string>>(L, -1);
647 auto iter = std::find(names.begin(), names.end(), k);
648 if(iter != names.end()) {
649 int i = std::distance(names.begin(), iter) + 1;
650 lua_rawgeti(L, 1, i);
651 return 1;
652 }
653 }
654 return 0;
655}
656
658{
659 if(lua_type(L, 2) == LUA_TSTRING) {
660 std::string k = lua_tostring(L, 2);
661 luaL_getmetafield(L, 1, "__names");
662 auto names = lua_check<std::vector<std::string>>(L, -1);
663 auto iter = std::find(names.begin(), names.end(), k);
664 if(iter != names.end()) {
665 int i = std::distance(names.begin(), iter) + 1;
666 lua_pushvalue(L, 3);
667 lua_rawseti(L, 1, i);
668 return 0;
669 }
670 }
671 // If it's not one of the special names, just assign normally
672 lua_settop(L, 3);
673 lua_rawset(L, 1);
674 return 0;
675}
676
678{
679 luaL_getmetafield(L, 1, "__names");
680 return 1;
681}
682
684{
685 std::vector<std::string> elems;
686 for(unsigned i = 1; i <= lua_rawlen(L, 1); i++) {
687 lua_getglobal(L, "tostring");
688 lua_rawgeti(L, 1, i);
689 lua_call(L, 1, 1);
690 elems.push_back(lua_tostring(L, -1));
691 }
692 lua_push(L, "(" + utils::join(elems) + ")");
693 return 1;
694}
695
697 // Comparing a named tuple with any other table is always false.
698 if(lua_type(L, 1) != LUA_TTABLE || lua_type(L, 2) != LUA_TTABLE) {
699 NOT_EQUAL:
700 lua_pushboolean(L, false);
701 return 1;
702 }
703 luaL_getmetafield(L, 1, "__name");
704 luaL_getmetafield(L, 2, "__name");
705 if(!lua_rawequal(L, 3, 4)) goto NOT_EQUAL;
706 lua_pop(L, 2);
707 // Named tuples can be equal only if they both have the exact same set of names.
708 luaL_getmetafield(L, 1, "__names");
709 luaL_getmetafield(L, 2, "__names");
710 auto lnames = lua_check<std::vector<std::string>>(L, 3);
711 auto rnames = lua_check<std::vector<std::string>>(L, 4);
712 if(lnames != rnames) goto NOT_EQUAL;
713 lua_pop(L, 2);
714 // They are equal if all of the corresponding members in each tuple are equal.
715 for(std::size_t i = 1; i <= lnames.size(); i++) {
716 lua_rawgeti(L, 1, i);
717 lua_rawgeti(L, 2, i);
718 if(!lua_compare(L, 3, 4, LUA_OPEQ)) goto NOT_EQUAL;
719 lua_pop(L, 2);
720 }
721 // Theoretically, they could have other members besides the special named ones.
722 // But we ignore those for the purposes of equality.
723 lua_pushboolean(L, true);
724 return 1;
725}
726
728 : names_{ names }
729 , key_{ build_key(names) }
730{
731}
732
733void lua_named_tuple_builder::push(lua_State *L) const
734{
735 lua_createtable(L, names_.size(), 0);
736
737 if (luaL_newmetatable(L, key_.c_str()) != 0) {
738 static luaL_Reg callbacks[] = {
739 { "__index", &impl_get },
740 { "__newindex", &impl_set },
741 { "__dir", &impl_dir },
742 { "__eq", &impl_compare },
743 { "__tostring", &impl_tostring },
744 { nullptr, nullptr }
745 };
746 luaL_setfuncs(L, callbacks, 0);
747
748 lua_pushlstring(L, key_.c_str(), key_.size());
749 lua_setfield(L, -2, "__metatable");
750 lua_push(L, names_);
751 lua_setfield(L, -2, "__names");
752 // __name has already been set to KEY by luaL_newmetatable
753 }
754
755 lua_setmetatable(L, -2);
756}
757
758std::string lua_named_tuple_builder::build_key(const std::vector<std::string>& names)
759{
760 std::ostringstream str;
761 str << base_name_ << '(';
762 if(!names.empty()) {
763 str << names[0];
764 }
765 for(std::size_t i = 1; i < names.size(); i++) {
766 str << ", " << names[i];
767 }
768 str << ')';
769 return str.str();
770}
771
772std::vector<std::string> luaW_to_namedtuple(lua_State* L, int idx) {
773 std::vector<std::string> names;
774 if(luaL_getmetafield(L, idx, "__name")) {
775 if(lua_check<std::string>(L, -1) == "named tuple") {
776 luaL_getmetafield(L, idx, "__names");
777 names = lua_check<std::vector<std::string>>(L, -1);
778 lua_pop(L, 1);
779 }
780 lua_pop(L, 1);
781 }
782 return names;
783}
784
785void luaW_pushlocation(lua_State *L, const map_location& ml)
786{
787 static const lua_named_tuple_builder tuple_builder{ {"x", "y"} };
788
789 tuple_builder.push(L);
790
791 lua_pushinteger(L, ml.wml_x());
792 lua_rawseti(L, -2, 1);
793
794 lua_pushinteger(L, ml.wml_y());
795 lua_rawseti(L, -2, 2);
796}
797
798bool luaW_tolocation(lua_State *L, int index, map_location& loc) {
799 if (!lua_checkstack(L, LUA_MINSTACK)) {
800 return false;
801 }
802 if (lua_isnoneornil(L, index)) {
803 // Need this special check because luaW_tovconfig returns true in this case
804 return false;
805 }
806
808
809 index = lua_absindex(L, index);
810
811 if (lua_istable(L, index) || lua_isuserdata(L, index)) {
812 map_location result;
813 int x_was_num = 0, y_was_num = 0;
814 lua_getfield(L, index, "x");
815 result.set_wml_x(lua_tointegerx(L, -1, &x_was_num));
816 lua_getfield(L, index, "y");
817 result.set_wml_y(lua_tointegerx(L, -1, &y_was_num));
818 lua_pop(L, 2);
819 if (!x_was_num || !y_was_num) {
820 // If we get here and it was userdata, checking numeric indices won't help
821 // (It won't help if it was a WML table either, but there's no easy way to check that.)
822 if (lua_isuserdata(L, index)) {
823 return false;
824 }
825 lua_rawgeti(L, index, 1);
826 result.set_wml_x(lua_tointegerx(L, -1, &x_was_num));
827 lua_rawgeti(L, index, 2);
828 result.set_wml_y(lua_tointegerx(L, -1, &y_was_num));
829 lua_pop(L, 2);
830 }
831 if (x_was_num && y_was_num) {
832 loc = result;
833 return true;
834 }
835 } else if (lua_isnumber(L, index) && lua_isnumber(L, index + 1)) {
836 // If it's a number, then we consume two elements on the stack
837 // Since we have no way of notifying the caller that we have
838 // done this, we remove the first number from the stack.
839 loc.set_wml_x(lua_tointeger(L, index));
840 lua_remove(L, index);
841 loc.set_wml_y(lua_tointeger(L, index));
842 return true;
843 }
844 return false;
845}
846
847map_location luaW_checklocation(lua_State *L, int index)
848{
849 map_location result;
850 if (!luaW_tolocation(L, index, result))
851 luaW_type_error(L, index, "location");
852 return result;
853}
854
855int luaW_push_locationset(lua_State* L, const std::set<map_location>& locs)
856{
857 lua_createtable(L, locs.size(), 0);
858 int i = 1;
859 for(const map_location& loc : locs) {
861 lua_rawseti(L, -2, i);
862 ++i;
863 }
864 return 1;
865}
866
867std::set<map_location> luaW_check_locationset(lua_State* L, int idx)
868{
869 std::set<map_location> locs;
870 if(!lua_istable(L, idx)) {
871 luaW_type_error(L, idx, "array of locations");
872 }
873 lua_len(L, idx);
874 int len = luaL_checkinteger(L, -1);
875 for(int i = 1; i <= len; i++) {
876 const auto arg = scoped_lua_argument{L, idx, i};
877 locs.insert(luaW_checklocation(L, -1));
878 }
879 return locs;
880
881}
882
883void luaW_pushconfig(lua_State *L, const config& cfg)
884{
885 lua_newtable(L);
887}
888
890 return {L};
891}
892
893std::ostream& operator<<(std::ostream& os, const luaW_PrintStack& s) {
894 int top = lua_gettop(s.L);
895 os << "Lua Stack\n";
896 for(int i = 1; i <= top; i++) {
897 luaW_getglobal(s.L, "wesnoth", "as_text");
898 lua_pushvalue(s.L, i);
899 lua_call(s.L, 1, 1);
900 auto value = luaL_checkstring(s.L, -1);
901 lua_pop(s.L, 1);
902 os << '[' << i << ']' << value << '\n';
903 }
904 if(top == 0) os << "(empty)\n";
905 os << std::flush;
906 return os;
907}
908
909#define return_misformed() \
910 do { lua_settop(L, initial_top); return false; } while (0)
911
912bool luaW_toconfig(lua_State *L, int index, config &cfg)
913{
914 cfg.clear();
915 if (!lua_checkstack(L, LUA_MINSTACK))
916 return false;
917
918 // Get the absolute index of the table.
919 index = lua_absindex(L, index);
920 int initial_top = lua_gettop(L);
921
922 switch (lua_type(L, index))
923 {
924 case LUA_TTABLE:
925 break;
926 case LUA_TUSERDATA:
927 {
928 if (vconfig * ptr = static_cast<vconfig *> (luaL_testudata(L, index, vconfigKey))) {
929 cfg = ptr->get_parsed_config();
930 return true;
931 } else {
932 return false;
933 }
934 }
935 case LUA_TNONE:
936 case LUA_TNIL:
937 return true;
938 default:
939 return false;
940 }
941
942 // First convert the children (integer indices).
943 for (int i = 1, i_end = lua_rawlen(L, index); i <= i_end; ++i)
944 {
945 lua_rawgeti(L, index, i);
946 if (!lua_istable(L, -1)) return_misformed();
947 lua_rawgeti(L, -1, 1);
948 char const *m = lua_tostring(L, -1);
949 if (!m || !config::valid_tag(m)) return_misformed();
950 lua_rawgeti(L, -2, 2);
951 if (!luaW_toconfig(L, -1, cfg.add_child(m)))
953 lua_pop(L, 3);
954 }
955
956 // Then convert the attributes (string indices).
957 for (lua_pushnil(L); lua_next(L, index); lua_pop(L, 1))
958 {
959 int indextype = lua_type(L, -2);
960 if (indextype == LUA_TNUMBER) continue;
961 if (indextype != LUA_TSTRING) return_misformed();
962 const char* m = lua_tostring(L, -2);
965 if (lua_istable(L, -1)) {
966 int subindex = lua_absindex(L, -1);
967 std::ostringstream str;
968 for (int i = 1, i_end = lua_rawlen(L, subindex); i <= i_end; ++i, lua_pop(L, 1)) {
969 lua_rawgeti(L, -1, i);
971 if (!luaW_toscalar(L, -1, item)) return_misformed();
972 if (i > 1) str << ',';
973 str << item;
974 }
975 // If there are any string keys, it's malformed
976 for (lua_pushnil(L); lua_next(L, subindex); lua_pop(L, 1)) {
977 if (lua_type(L, -2) != LUA_TNUMBER) return_misformed();
978 }
979 v = str.str();
980 } else if (!luaW_toscalar(L, -1, v)) return_misformed();
981 }
982
983 lua_settop(L, initial_top);
984 return true;
985}
986
987#undef return_misformed
988
989
990config luaW_checkconfig(lua_State *L, int index)
991{
992 config result;
993 if (!luaW_toconfig(L, index, result))
994 luaW_type_error(L, index, "WML table");
995 return result;
996}
997
998config luaW_checkconfig(lua_State *L, int index, const vconfig*& vcfg)
999{
1000 config result = luaW_checkconfig(L, index);
1001 if(void* p = luaL_testudata(L, index, vconfigKey)) {
1002 vcfg = static_cast<vconfig*>(p);
1003 }
1004 return result;
1005}
1006
1007bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
1008{
1009 switch (lua_type(L, index))
1010 {
1011 case LUA_TTABLE:
1012 {
1013 config cfg;
1014 bool ok = luaW_toconfig(L, index, cfg);
1015 if (!ok) return false;
1016 vcfg = vconfig(std::move(cfg));
1017 break;
1018 }
1019 case LUA_TUSERDATA:
1020 if (vconfig * ptr = static_cast<vconfig *> (luaL_testudata(L, index, vconfigKey))) {
1021 vcfg = *ptr;
1022 } else {
1023 return false;
1024 }
1025 case LUA_TNONE:
1026 case LUA_TNIL:
1027 break;
1028 default:
1029 return false;
1030 }
1031 return true;
1032}
1033
1034vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing)
1035{
1037 if (!luaW_tovconfig(L, index, result) || (!allow_missing && result.null()))
1038 luaW_type_error(L, index, "WML table");
1039 return result;
1040}
1041
1042bool luaW_getglobal(lua_State *L, const std::vector<std::string>& path)
1043{
1044 lua_pushglobaltable(L);
1045 for (const std::string& s : path)
1046 {
1047 if (!lua_istable(L, -1)) goto discard;
1048 lua_pushlstring(L, s.c_str(), s.size());
1049 lua_rawget(L, -2);
1050 lua_remove(L, -2);
1051 }
1052
1053 if (lua_isnil(L, -1)) {
1054 discard:
1055 lua_pop(L, 1);
1056 return false;
1057 }
1058 return true;
1059}
1060
1061bool luaW_toboolean(lua_State *L, int n)
1062{
1063 return lua_toboolean(L,n) != 0;
1064}
1065
1067{
1068 try
1069 {
1070 if(v.exists_as_attribute())
1071 {
1072 luaW_pushscalar(L, v.as_scalar());
1073 return true;
1074 }
1075 else if(v.exists_as_container())
1076 {
1077 lua_newtable(L);
1079 return true;
1080 }
1081 else
1082 {
1083 lua_pushnil(L);
1084 return true;
1085 }
1086 }
1087 catch (const invalid_variablename_exception&)
1088 {
1090 return false;
1091 }
1092}
1093
1095{
1096 int variabletype = lua_type(L, n);
1097 try
1098 {
1099 switch (variabletype) {
1100 case LUA_TBOOLEAN:
1101 v.as_scalar() = luaW_toboolean(L, n);
1102 return true;
1103 case LUA_TNUMBER:
1104 v.as_scalar() = lua_tonumber(L, n);
1105 return true;
1106 case LUA_TSTRING:
1107 v.as_scalar() = std::string(luaW_tostring(L, n));
1108 return true;
1109 case LUA_TUSERDATA:
1110 if (t_string * t_str = static_cast<t_string*> (luaL_testudata(L, n, tstringKey))) {
1111 v.as_scalar() = *t_str;
1112 return true;
1113 }
1114 goto default_explicit;
1115 case LUA_TTABLE:
1116 {
1117 config &cfg = v.as_container();
1118 if (luaW_toconfig(L, n, cfg)) {
1119 return true;
1120 }
1121 [[fallthrough]];
1122 }
1123 default:
1124 default_explicit:
1125 return luaW_type_error(L, n, "WML table or scalar") != 0;
1126
1127 }
1128 }
1129 catch (const invalid_variablename_exception&)
1130 {
1131 WRN_LUA << v.get_error_message() << " when attempting to write a '" << lua_typename(L, variabletype) << "'";
1132 return false;
1133 }
1134}
1135
1136bool luaW_tableget(lua_State *L, int index, const char* key)
1137{
1138 index = lua_absindex(L, index);
1139 lua_pushstring(L, key);
1140 lua_gettable(L, index);
1141 if(lua_isnoneornil(L, -1)) {
1142 lua_pop(L, 1);
1143 return false;
1144 }
1145 return true;
1146}
1147
1148std::string_view luaW_tostring(lua_State *L, int index)
1149{
1150 std::size_t len = 0;
1151 const char* str = lua_tolstring(L, index, &len);
1152 if(!str) {
1153 throw luaL_error (L, "not a string");
1154 }
1155 return std::string_view(str, len);
1156}
1157
1158std::string_view luaW_tostring_or_default(lua_State *L, int index, std::string_view def)
1159{
1160 std::size_t len = 0;
1161 const char* str = lua_tolstring(L, index, &len);
1162 if(!str) {
1163 return def;
1164 }
1165 return std::string_view(str, len);
1166}
1167
1168void chat_message(const std::string& caption, const std::string& msg)
1169{
1170 if (!game_display::get_singleton()) return;
1172 std::chrono::system_clock::now(), caption, 0, msg, events::chat_handler::MESSAGE_PUBLIC, false);
1173}
1174
1175void push_error_handler(lua_State *L)
1176{
1177 luaW_getglobal(L, "debug", "traceback");
1178 lua_setfield(L, LUA_REGISTRYINDEX, executeKey);
1179}
1180
1181int luaW_pcall_internal(lua_State *L, int nArgs, int nRets)
1182{
1183 // Load the error handler before the function and its arguments.
1184 lua_getfield(L, LUA_REGISTRYINDEX, executeKey);
1185 lua_insert(L, -2 - nArgs);
1186
1187 int error_handler_index = lua_gettop(L) - nArgs - 1;
1188
1190
1191 // Call the function.
1192 int errcode = lua_pcall(L, nArgs, nRets, -2 - nArgs);
1193
1196
1197 // Remove the error handler.
1198 lua_remove(L, error_handler_index);
1199
1200 return errcode;
1201}
1202
1203#ifdef _MSC_VER
1204#pragma warning (push)
1205#pragma warning (disable: 4706)
1206#endif
1207bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
1208{
1209 int res = luaW_pcall_internal(L, nArgs, nRets);
1210
1211 if (res)
1212 {
1213 /*
1214 * When an exception is thrown which doesn't derive from
1215 * std::exception m will be nullptr pointer.
1216 * When adding a new conditional branch, remember to log the
1217 * error with ERR_LUA or ERR_WML.
1218 */
1219 char const *m = lua_tostring(L, -1);
1220 if(m) {
1221 if (allow_wml_error && strncmp(m, "~wml:", 5) == 0) {
1222 m += 5;
1223 char const *e = strstr(m, "stack traceback");
1224 lg::log_to_chat() << std::string(m, e ? e - m : strlen(m)) << '\n';
1225 ERR_WML << std::string(m, e ? e - m : strlen(m));
1226 } else if (allow_wml_error && strncmp(m, "~lua:", 5) == 0) {
1227 m += 5;
1228 char const *e = nullptr, *em = m;
1229 while (em[0] && ((em = strstr(em + 1, "stack traceback"))))
1230#ifdef _MSC_VER
1231#pragma warning (pop)
1232#endif
1233 e = em;
1234 ERR_LUA << std::string(m, e ? e - m : strlen(m));
1235 chat_message("Lua error", std::string(m, e ? e - m : strlen(m)));
1236 } else {
1237 ERR_LUA << m;
1238 chat_message("Lua error", m);
1239 }
1240 } else {
1241 ERR_LUA << "Lua caught unknown exception";
1242 chat_message("Lua caught unknown exception", "");
1243 }
1244 lua_pop(L, 1);
1245 return false;
1246 }
1247
1248 return true;
1249}
1250
1251// Originally luaL_typerror, now deprecated.
1252// Easier to define it for Wesnoth and not have to worry about it if we update Lua.
1253int luaW_type_error(lua_State *L, int narg, const char *tname) {
1254 const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg));
1255 return luaL_argerror(L, narg, msg);
1256}
1257
1258// An alternate version which raises an error for a key in a table.
1259// In this version, narg should refer to the stack index of the table rather than the stack index of the key.
1260// kpath should be the key name or a string such as "key[idx].key2" specifying a path to the key.
1261int luaW_type_error (lua_State *L, int narg, const char* kpath, const char *tname) {
1262 const char *msg = lua_pushfstring(L, "%s expected for '%s', got %s", tname, kpath, luaL_typename(L, narg));
1263 return luaL_argerror(L, narg, msg);
1264}
map_location loc
Definition move.cpp:172
double t
std::vector< std::string > names
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
static bool valid_tag(std::string_view name)
Definition config.cpp:129
config & add_child(std::string_view key)
Definition config.cpp:436
const_attr_itors attribute_range() const
Definition config.cpp:740
auto all_children_view() const
In-order iteration over all children.
Definition config.hpp:795
static bool valid_attribute(std::string_view name)
Definition config.cpp:152
boost::iterator_range< const_attribute_iterator > const_attr_itors
Definition config.hpp:357
attribute_map::value_type attribute
Definition config.hpp:297
void clear()
Definition config.cpp:802
void add_chat_message(const std::chrono::system_clock::time_point &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
static game_display * get_singleton()
display_chat_manager & get_chat_manager()
static void rethrow()
Rethrows the stored exception.
static int jail_depth
Depth of recursive luaW_pcall_internal() function calls.
Efficiently creates new LUA "named tuples" with the specified field names.
static int impl_set(lua_State *L)
static std::string build_key(const std::vector< std::string > &names)
static int impl_tostring(lua_State *L)
lua_named_tuple_builder(const std::vector< std::string > &names)
static int impl_compare(lua_State *L)
void push(lua_State *L) const
Push an empty "named tuple" onto the stack.
static int impl_dir(lua_State *L)
const std::vector< std::string > names_
static int impl_get(lua_State *L)
static constexpr std::string_view base_name_
const std::string key_
Shallow wrapper around lua_geti which pops the top variable from the Lua stack when destroyed.
scoped_lua_argument(lua_State *L, int arg_index)
lua_State *const state_
const t_string_base & get() const
Definition tstring.hpp:214
Additional functionality for a non-const variable_info.
Information on a WML variable.
maybe_const_t< config::attribute_value, V > & as_scalar() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
std::string get_error_message() const
bool exists_as_container() const
maybe_const_t< config, V > & as_container() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
bool exists_as_attribute() const
A variable-expanding proxy for the config class.
Definition variable.hpp:45
~vconfig()
Default destructor, but defined here for possibly faster compiles (templates sometimes can be rough o...
Definition variable.cpp:138
all_children_iterator ordered_begin() const
In-order iteration over all children.
Definition variable.cpp:481
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition variable.hpp:99
static vconfig unconstructed_vconfig()
This is just a wrapper for the default constructor; it exists for historical reasons and to make it c...
Definition variable.cpp:153
bool null() const
Definition variable.hpp:72
config::attribute_value expand(const std::string &) const
Definition variable.cpp:353
config get_parsed_config() const
Definition variable.cpp:177
all_children_iterator ordered_end() const
Definition variable.cpp:486
const config & get_config() const
Definition variable.hpp:75
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
Standard logging facilities (interface).
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
int luaW_pcall_internal(lua_State *L, int nArgs, int nRets)
void luaW_filltable(lua_State *L, const config &cfg)
Converts a config object to a Lua table.
void chat_message(const std::string &caption, const std::string &msg)
Displays a message in the chat window.
#define ERR_LUA
void push_error_handler(lua_State *L)
static const char executeKey[]
static lg::log_domain log_scripting_lua("scripting/lua")
bool luaW_iststring(lua_State *L, int index)
static const char vconfigKey[]
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
void luaW_pushlocation(lua_State *L, const map_location &ml)
Converts a map location object to a Lua table pushed at the top of the stack.
void luaW_pushtstring(lua_State *L, const t_string &v)
Pushes a t_string on the top of the stack.
bool luaW_pushvariable(lua_State *L, variable_access_const &v)
bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
Gets an optional vconfig from either a table or a userdata.
#define ERR_WML
std::ostream & operator<<(std::ostream &os, const luaW_PrintStack &s)
void luaW_pushvconfig(lua_State *L, const vconfig &cfg)
Pushes a vconfig on the top of the stack.
bool luaW_toboolean(lua_State *L, int n)
static const char vconfigpairsKey[]
int luaW_type_error(lua_State *L, int narg, const char *tname)
#define return_misformed()
bool luaW_toscalar(lua_State *L, int index, config::attribute_value &v)
Converts the value at the top of the stack to an attribute value.
std::string_view luaW_tostring(lua_State *L, int index)
static const char tstringKey[]
bool luaW_getmetafield(lua_State *L, int idx, const char *key)
Like luaL_getmetafield, but returns false if key is an empty string or begins with two underscores.
#define WRN_LUA
static const char gettextKey[]
void luaW_pushscalar(lua_State *L, const config::attribute_value &v)
Converts an attribute value into a Lua object pushed at the top of the stack.
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
std::vector< std::string > luaW_to_namedtuple(lua_State *L, int idx)
Get the keys of a "named tuple" from the stack.
bool luaW_tableget(lua_State *L, int index, const char *key)
std::string_view luaW_tostring_or_default(lua_State *L, int index, std::string_view def)
bool luaW_checkvariable(lua_State *L, variable_access_create &v, int n)
static lg::log_domain log_wml("wml")
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
int luaW_push_locationset(lua_State *L, const std::set< map_location > &locs)
Converts a set of map locations to a Lua table pushed at the top of the stack.
vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing)
Gets an optional vconfig from either a table or a userdata.
std::set< map_location > luaW_check_locationset(lua_State *L, int idx)
Converts a table of integer pairs to a set of map location objects.
bool luaW_tolocation(lua_State *L, int index, map_location &loc)
Converts an optional table or pair of integers to a map location object.
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
luaW_PrintStack luaW_debugstack(lua_State *L)
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
t_string luaW_checktstring(lua_State *L, int index)
Converts a scalar to a translatable string.
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition log.cpp:550
static int impl_tstring_concat(lua_State *L)
Appends a scalar to a t_string object (__concat metamethod).
int intf_textdomain(lua_State *L)
Creates an interface for gettext.
static int impl_vconfig_pairs(lua_State *L)
Construct an iterator to iterate through the attributes of a vconfig.
static int impl_tstring_collect(lua_State *L)
Destroys a t_string object before it is collected (__gc metamethod).
static int impl_tstring_eq(lua_State *L)
std::string register_gettext_metatable(lua_State *L)
Adds the gettext metatable.
static int impl_tstring_lt(lua_State *L)
std::string register_vconfig_metatable(lua_State *L)
Adds the vconfig metatable.
static int impl_vconfig_get(lua_State *L)
Gets the parsed field of a vconfig object (_index metamethod).
static int impl_vconfig_pairs_collect(lua_State *L)
Destroy a vconfig pairs iterator.
std::string register_tstring_metatable(lua_State *L)
Adds the tstring metatable.
static int impl_gettext(lua_State *L)
Creates a t_string object (__call metamethod).
static int impl_gettext_tostr(lua_State *L)
static int impl_tstring_tostring(lua_State *L)
Converts a t_string object to a string (__tostring metamethod); that is, performs a translation.
static int impl_vconfig_size(lua_State *L)
Returns the number of a child of a vconfig object.
int intf_tovconfig(lua_State *L)
Creates a vconfig containing the WML table.
static int impl_tstring_len(lua_State *L)
static int impl_vconfig_pairs_iter(lua_State *L)
Iterate through the attributes of a vconfig.
static int impl_vconfig_dir(lua_State *L)
static int impl_vconfig_collect(lua_State *L)
Destroys a vconfig object before it is collected (__gc metamethod).
static int impl_tstring_le(lua_State *L)
static void tstring_concat_aux(lua_State *L, t_string &dst, int src)
Converts a Lua value at position src and appends it to dst.
int compare(const std::string &s1, const std::string &s2)
Case-sensitive lexicographical comparison.
Definition gettext.cpp:502
std::string join(const Range &v, const std::string &s=",")
Generates a new string joining container items in a list.
void lua_push(lua_State *L, const T &val)
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
Encapsulates the map of the game.
Definition location.hpp:46
void set_wml_y(int v)
Definition location.hpp:190
int wml_y() const
Definition location.hpp:187
void set_wml_x(int v)
Definition location.hpp:189
int wml_x() const
Definition location.hpp:186
mock_party p
static map_location::direction n
static map_location::direction s
#define i
#define d
#define e
#define b