The Battle for Wesnoth  1.15.10+dev
lua_terrainmap.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2018 the Battle for Wesnoth Project https://www.wesnoth.org/
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY.
10 
11  See the COPYING file for more details.
12 */
13 
16 
17 #include "formatter.hpp"
18 #include "global.hpp"
19 #include "log.hpp"
20 #include "map/location.hpp"
21 #include "map/map.hpp"
22 #include "scripting/lua_common.hpp"
23 #include "scripting/push_check.hpp"
25 
26 #include "lua/lauxlib.h"
27 #include "lua/lua.h"
28 
29 static lg::log_domain log_scripting_lua("scripting/lua");
30 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
31 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
32 
33 static const char terrinmapKey[] = "terrainmap";
34 static const char maplocationKey[] = "special_locations";
35 
36 using std::string_view;
37 
38 //////// SPECIAL LOCATION ////////
39 
41 {
42  return luaL_testudata(L, index, maplocationKey) != nullptr;
43 }
44 
45 
47 {
48  if(!lua_istable(L, index)) {
49  return nullptr;
50  }
51 
52  lua_rawgeti(L, index, 1);
54  lua_pop(L, 1);
55  return m;
56 }
57 
59 {
60  if(mapgen_gamemap* m = luaW_toslocs(L, index)) {
61  return *m;
62  }
63  luaW_type_error(L, index, "terrainmap");
64  throw "luaW_type_error didn't thow.";
65 }
66 
68 {
70 }
71 /**
72  * @a index the index of the map object.
73  */
75 {
76  lua_pushvalue(L, index);
77  //stack: map
78  lua_createtable(L, 1, 0);
79  //stack: map, slocs
80  lua_pushvalue(L, -2);
81  //stack: map, slocs, map
82  lua_rawseti(L, -2, 1);
83  //stack: map, slocs
85  //stack: map, slocs
86  lua_remove(L, -2);
87  //stack: slocs
88 }
89 
91 {
92  //todo: calling map.special_locations[1] will return the underlying map
93  // object instead of the first starting position, because the lua
94  // special locations is actually a table with the map object at
95  // index 1. The probably easiest way to fix this inconsitency is
96  // to just disallow all integerindicies here.
98  string_view id = luaL_checkstring(L, 2);
99  auto res = m.special_location(std::string(id));
100  if(res.wml_x() >= 0) {
101  luaW_pushlocation(L, res);
102  }
103  else {
104  //functions with variable return numbers have been causing problem in the past
105  lua_pushnil(L);
106  }
107  return 1;
108 }
109 
111 {
112  mapgen_gamemap& m = luaW_check_slocs(L, 1);
113  string_view id = luaL_checkstring(L, 2);
114  map_location loc = luaW_checklocation(L, 3);
115 
116  m.set_special_location(std::string(id), loc);
117  return 0;
118 }
119 
120 //////// MAP ////////
121 
123  : tiles_()
124  , starting_positions_()
125 {
126  if(s.empty()) {
127  return;
128  }
129  //throws t_translation::error
130  //todo: make read_game_map take a string_view
132 }
133 
135  : tiles_(w, h, t)
137 {
138 
139 }
140 
141 std::string mapgen_gamemap::to_string() const
142 {
143  return t_translation::write_game_map(tiles_, starting_positions_, { 1, 1 }) + "\n";
144 }
145 
147 {
148  terrain_code& t = (*this)[loc];
149  terrain_code old = t;
150  t = terrain;
151  simplemerge(old, t, mode);
152 
153 }
154 
156 {
157  if(mode == terrain_type_data::OVERLAY) {
158  new_t = t_translation::terrain_code(old_t.base, new_t.overlay);
159  }
160  if(mode == terrain_type_data::BASE) {
161  new_t = t_translation::terrain_code(new_t.base, old_t.overlay);
162  }
163 }
164 
165 void mapgen_gamemap::set_special_location(const std::string& id, const map_location& loc)
166 {
167  bool valid = loc.valid();
168  auto it_left = starting_positions_.left.find(id);
169  if (it_left != starting_positions_.left.end()) {
170  if (valid) {
171  starting_positions_.left.replace_data(it_left, loc);
172  }
173  else {
174  starting_positions_.left.erase(it_left);
175  }
176  }
177  else {
178  starting_positions_.left.insert(it_left, std::pair(id, loc));
179  }
180 }
181 
182 map_location mapgen_gamemap::special_location(const std::string& id) const
183 {
184  auto it = starting_positions_.left.find(id);
185  if (it != starting_positions_.left.end()) {
186  auto& coordinate = it->second;
188  }
189  else {
190  return map_location();
191  }
192 }
193 
195 {
196  return luaL_testudata(L, index, terrinmapKey) != nullptr;
197 }
198 
199 
201 {
202  if(luaW_isterrainmap(L, index)) {
203  return static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
204  }
205  return nullptr;
206 }
207 
209 {
210  if(luaW_isterrainmap(L, index)) {
211  return *static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
212  }
213  luaW_type_error(L, index, "terrainmap");
214  throw "luaW_type_error didn't throw";
215 }
216 
218 {
220 }
221 
222 template<typename... T>
224 {
225  mapgen_gamemap* res = new(L) mapgen_gamemap(std::forward<T>(params)...);
227  return res;
228 }
229 
230 /**
231  * Create a map.
232  * - Arg 1: string descripbing the map data.
233  * - or:
234  * - Arg 1: int, width
235  * - Arg 2: int, height
236  * - Arg 3: string, terrain
237 */
239 {
240  if(lua_isnumber(L, 1) && lua_isnumber(L, 2)) {
241  int w = lua_tonumber(L, 1);
242  int h = lua_tonumber(L, 2);
244  luaW_pushmap(L, w, h, terrain);
245  return 1;
246  }
247  else {
248  string_view data_str = luaL_checkstring(L, 1);
249  luaW_pushmap(L, data_str);
250  return 1;
251  }
252 }
253 
254 /**
255  * Destroys a map object before it is collected (__gc metamethod).
256  */
258 {
259  mapgen_gamemap *u = static_cast<mapgen_gamemap*>(lua_touserdata(L, 1));
260  u->mapgen_gamemap::~mapgen_gamemap();
261  return 0;
262 }
263 
264 /**
265  * Gets some data on a map (__index metamethod).
266  * - Arg 1: full userdata containing the map.
267  * - Arg 2: string containing the name of the property.
268  * - Ret 1: something containing the attribute.
269  */
271 {
273  char const *m = luaL_checkstring(L, 2);
274 
275  // Find the corresponding attribute.
276  return_int_attrib("width", tm.total_width());
277  return_int_attrib("height", tm.total_height());
278  return_string_attrib("data", tm.to_string());
279 
280  if(strcmp(m, "special_locations") == 0) {
281  luaW_pushslocs(L, 1);
282  return 1;
283  }
284  if(luaW_getmetafield(L, 1, m)) {
285  return 1;
286  }
287  return 0;
288 }
289 
290 /**
291  * Sets some data on a map (__newindex metamethod).
292  * - Arg 1: full userdata containing the map.
293  * - Arg 2: string containing the name of the property.
294  * - Arg 3: something containing the attribute.
295  */
297 {
299  UNUSED(tm);
300  char const *m = luaL_checkstring(L, 2);
301  std::string err_msg = "unknown modifiable property of map: ";
302  err_msg += m;
303  return luaL_argerror(L, 2, err_msg.c_str());
304 }
305 
306 
307 /**
308  * Sets a terrain code.
309  * - Arg 1: map location.
310  * - Arg 2: terrain code string.
311  * - Arg 3: layer: (overlay|base|both, default=both)
312 */
314 {
316  map_location loc = luaW_checklocation(L, 2);
317  string_view t_str = luaL_checkstring(L, 3);
318 
319  auto terrain = t_translation::read_terrain_code(t_str);
320  auto mode = terrain_type_data::BOTH;
321 
322  if(!lua_isnoneornil(L, 4)) {
323  string_view mode_str = luaL_checkstring(L, 4);
324  if(mode_str == "base") {
326  }
327  else if(mode_str == "overlay") {
329  }
330  }
331 
332  tm.set_terrain(loc, terrain, mode);
333  return 0;
334 }
335 
336 /**
337  * Gets a terrain code.
338  * - Arg 1: map location.
339  * - Ret 1: string.
340  */
342 {
344  map_location loc = luaW_checklocation(L, 2);
345 
346  auto t = tm[loc];
348  return 1;
349 }
350 
351 static std::vector<gamemap::overlay_rule> read_rules_vector(lua_State *L, int index)
352 {
353  std::vector<gamemap::overlay_rule> rules;
354  for (int i = 1, i_end = lua_rawlen(L, index); i <= i_end; ++i)
355  {
356  lua_rawgeti(L, index, i);
357  if(!lua_istable(L, -1)) {
358  luaL_argerror(L, index, "rules must be a table of tables");
359  }
360  rules.push_back(gamemap::overlay_rule());
361  auto& rule = rules.back();
362  if(luaW_tableget(L, -1, "old")) {
363  rule.old_ = t_translation::read_list(luaW_tostring(L, -1));
364  lua_pop(L, 1);
365  }
366  if(luaW_tableget(L, -1, "new")) {
367  rule.new_ = t_translation::read_list(luaW_tostring(L, -1));
368  lua_pop(L, 1);
369  }
370 
371  if(luaW_tableget(L, -1, "mode")) {
372  auto str = luaW_tostring(L, -1);
373  rule.mode_ = str == "base" ? terrain_type_data::BASE : (str == "overlay" ? terrain_type_data::OVERLAY : terrain_type_data::BOTH);
374  lua_pop(L, 1);
375  }
376 
377  if(luaW_tableget(L, -1, "terrain")) {
379  if(!terrain.empty()) {
380  rule.terrain_ = terrain[0];
381  }
382  lua_pop(L, 1);
383  }
384 
385  if(luaW_tableget(L, -1, "use_old")) {
386  rule.use_old_ = luaW_toboolean(L, -1);
387  lua_pop(L, 1);
388  }
389 
390  if(luaW_tableget(L, -1, "replace_if_failed")) {
391  rule.replace_if_failed_ = luaW_toboolean(L, -1);
392  lua_pop(L, 1);
393  }
394 
395  lua_pop(L, 1);
396  }
397  return rules;
398 }
399 /**
400  * Reaplces part of the map.
401  * - Arg 1: map location.
402  * - Arg 2: map data string.
403  * - Arg 3: table for optional named arguments
404  * - is_odd: boolen, if Arg2 has the odd mapo format (as if it was cut from a odd map location)
405  * - ignore_special_locations: boolean
406  * - rules: table of tables
407 */
409 {
411  map_location loc = luaW_checklocation(L, 2);
413 
414  bool is_odd = false;
415  bool ignore_special_locations = false;
416  std::vector<gamemap::overlay_rule> rules;
417 
418  if(lua_istable(L, 4)) {
419  is_odd = luaW_table_get_def<bool>(L, 4, "is_odd", false);
420  ignore_special_locations = luaW_table_get_def<bool>(L, 4, "ignore_special_locations", false);
421 
422  if(luaW_tableget(L, 4, "rules")) {
423  if(!lua_istable(L, -1)) {
424  return luaL_argerror(L, 4, "rules must be a table");
425  }
426  rules = read_rules_vector(L, -1);
427  lua_pop(L, 1);
428  }
429  }
430 
432  tm1.tiles_,
434  tm2.tiles_,
436  [&](const map_location& loc, const t_translation::terrain_code& t, terrain_type_data::merge_mode mode, bool) { tm1.set_terrain(loc, t, mode); },
437  loc,
438  rules,
439  is_odd,
440  ignore_special_locations
441  );
442 
443  return 0;
444 }
445 
446 namespace lua_terrainmap {
448  {
449  std::ostringstream cmd_out;
450 
451  cmd_out << "Adding terrainmamap metatable...\n";
452 
455  lua_setfield(L, -2, "__gc");
457  lua_setfield(L, -2, "__index");
459  lua_setfield(L, -2, "__newindex");
460  lua_pushstring(L, "terainmap");
461  lua_setfield(L, -2, "__metatable");
462  // terainmap methods
464  lua_setfield(L, -2, "set_terrain");
466  lua_setfield(L, -2, "get_terrain");
468  lua_setfield(L, -2, "get_locations");
470  lua_setfield(L, -2, "get_tiles_radius");
472  lua_setfield(L, -2, "terrain_mask");
473 
474  cmd_out << "Adding terrainmamap2 metatable...\n";
475 
478  lua_setfield(L, -2, "__index");
480  lua_setfield(L, -2, "__newindex");
481  lua_pushstring(L, "special_locations");
482  lua_setfield(L, -2, "__metatable");
483 
484  return cmd_out.str();
485  }
486 }
static int intf_mg_terrain_mask(lua_State *L)
Reaplces part of the map.
bool luaW_tableget(lua_State *L, int index, const char *key)
Definition: lua_common.cpp:969
#define lua_isnoneornil(L, n)
Definition: lua.h:379
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:728
#define lua_pushcfunction(L, f)
Definition: lua.h:370
mapgen_gamemap * luaW_toslocs(lua_State *L, int index)
bool is_odd(T num)
Definition: math.hpp:35
mapgen_gamemap & luaW_check_slocs(lua_State *L, int index)
static const char maplocationKey[]
int luaW_type_error(lua_State *L, int narg, const char *tname)
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:710
void lua_slocs_setmetatable(lua_State *L)
std::string to_string() const
static void simplemerge(terrain_code old, terrain_code &t, const terrain_type_data::merge_mode mode)
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:889
#define lua_remove(L, idx)
Definition: lua.h:391
bool luaW_isterrainmap(lua_State *L, int index)
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:226
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
int impl_slocs_get(lua_State *L)
t_translation::ter_map tiles_
#define lua_tonumber(L, i)
Definition: lua.h:361
int h() const
Effective map height.
ter_map read_game_map(std::string_view str, starting_positions &starting_positions, coordinate border_offset)
Reads a gamemap string into a 2D vector.
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:324
void set_terrain(const map_location &loc, const terrain_code &terrain, const terrain_type_data::merge_mode mode)
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:725
void set_special_location(const std::string &id, const map_location &loc)
#define lua_pop(L, n)
Definition: lua.h:364
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
int intf_terainmap_create(lua_State *L)
Create a map.
static std::vector< gamemap::overlay_rule > read_rules_vector(lua_State *L, int index)
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:175
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:235
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:893
int intf_mg_get_tiles_radius(lua_State *L)
map_location special_location(const std::string &id) const
starting_positions starting_positions_
bool valid() const
Definition: location.hpp:88
void luaW_pushslocs(lua_State *L, int index)
index the index of the map object.
static int impl_terainmap_collect(lua_State *L)
Destroys a map object before it is collected (__gc metamethod).
static void overlay_impl(const t_translation::ter_map &m1, t_translation::starting_positions &m1_st, const t_translation::ter_map &m2, const t_translation::starting_positions &m2_st, std::function< void(const map_location &, const t_translation::terrain_code &, terrain_type_data::merge_mode, bool)> set_terrain, map_location loc, const std::vector< overlay_rule > &rules, bool is_odd, bool ignore_special_locations)
Definition: map.cpp:221
std::string register_metatables(lua_State *L)
LUALIB_API void * luaL_testudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:330
static lg::log_domain log_scripting_lua("scripting/lua")
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:473
std::string write_terrain_code(const terrain_code &tcode)
Writes a single terrain code to a string.
Encapsulates the map of the game.
Definition: location.hpp:37
#define UNUSED(x)
Definition: global.hpp:30
LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:311
LUA_API void * lua_touserdata(lua_State *L, int idx)
Definition: lapi.cpp:432
std::size_t i
Definition: function.cpp:934
static map_location::DIRECTION s
mapgen_gamemap(std::string_view data)
int w() const
Effective map width.
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:246
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:285
static int intf_set_terrain(lua_State *L)
Sets a terrain code.
std::string write_game_map(const ter_map &map, const starting_positions &starting_positions, coordinate border_offset)
Write a gamemap in to a vector string.
mapgen_gamemap * luaW_pushmap(lua_State *L, T &&... params)
LUA_API lua_Unsigned lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:402
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...
Definition: lua_common.cpp:512
mapgen_gamemap & luaW_checkterrainmap(lua_State *L, int index)
int total_height() const
Real height of the map, including borders.
mapgen_gamemap * luaW_toterrainmap(lua_State *L, int index)
double t
Definition: astarsearch.cpp:64
#define lua_istable(L, n)
Definition: lua.h:373
std::string_view luaW_tostring(lua_State *L, int index)
Definition: lua_common.cpp:981
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.
Definition: lua_common.cpp:665
static int impl_terainmap_set(lua_State *L)
Sets some data on a map (__newindex metamethod).
void lua_terrainmap_setmetatable(lua_State *L)
Standard logging facilities (interface).
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
int impl_slocs_set(lua_State *L)
bool luaW_isslocs(lua_State *L, int index)
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
int total_width() const
Real width of the map, including borders.
static int intf_get_terrain(lua_State *L)
Gets a terrain code.
int intf_mg_get_locations(lua_State *L)
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:514
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:837
static const char terrinmapKey[]
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
static int impl_terainmap_get(lua_State *L)
Gets some data on a map (__index metamethod).
#define luaL_checkstring(L, n)
Definition: lauxlib.h:138