The Battle for Wesnoth  1.15.2+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 "log.hpp"
19 #include "map/location.hpp"
20 #include "map/map.hpp"
21 #include "scripting/lua_common.hpp"
22 #include "scripting/push_check.hpp"
24 
25 #include "lua/lauxlib.h"
26 #include "lua/lua.h"
27 
28 static lg::log_domain log_scripting_lua("scripting/lua");
29 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
30 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
31 
32 static const char terrinmapKey[] = "terrainmap";
33 static const char maplocationKey[] = "special_locations";
34 
35 using utils::string_view;
36 
37 //////// SPECIAL LOCATION ////////
38 
40 {
41  return luaL_testudata(L, index, maplocationKey) != nullptr;
42 }
43 
44 
46 {
47  if(!lua_istable(L, index)) {
48  return nullptr;
49  }
50 
51  lua_rawgeti(L, index, 1);
53  lua_pop(L, 1);
54  return m;
55 }
56 
58 {
59  if(mapgen_gamemap* m = luaW_toslocs(L, index)) {
60  return *m;
61  }
62  luaW_type_error(L, index, "terrainmap");
63  throw "luaW_type_error didn't thow.";
64 }
65 
67 {
69 }
70 /**
71  * @a index the index of the map object.
72  */
74 {
75  lua_pushvalue(L, index);
76  //stack: map
77  lua_createtable(L, 1, 0);
78  //stack: map, slocs
79  lua_pushvalue(L, -2);
80  //stack: map, slocs, map
81  lua_rawseti(L, -2, 1);
82  //stack: map, slocs
84  //stack: map, slocs
85  lua_remove(L, -2);
86  //stack: slocs
87 }
88 
90 {
91  //todo: calling map.special_locations[1] will return the underlying map
92  // object instead of the first starting position, because the lua
93  // special locations is actually a table with the map object at
94  // index 1. The probably easiest way to fix this inconsitency is
95  // to just disallow all integerindicies here.
97  string_view id = luaL_checkstring(L, 2);
98  auto res = m.special_location(std::string(id));
99  if(res.wml_x() >= 0) {
100  luaW_pushlocation(L, res);
101  }
102  else {
103  //functions with variable return numbers have been causing problem in the past
104  lua_pushnil(L);
105  }
106  return 1;
107 }
108 
110 {
111  mapgen_gamemap& m = luaW_check_slocs(L, 1);
112  string_view id = luaL_checkstring(L, 2);
113  map_location loc = luaW_checklocation(L, 3);
114 
115  m.set_special_location(std::string(id), loc);
116  return 0;
117 }
118 
119 //////// MAP ////////
120 
122  : tiles_()
123  , starting_positions_()
124 {
125  if(s.empty()) {
126  return;
127  }
128  //throws t_translation::error
129  //todo: make read_game_map take a string_view
130  tiles_ = t_translation::read_game_map(s, starting_positions_, t_translation::coordinate{ 1, 1 });
131 }
132 
134  : tiles_(w, h, t)
135  , starting_positions_()
136 {
137 
138 }
139 
140 std::string mapgen_gamemap::to_string() const
141 {
142  return t_translation::write_game_map(tiles_, starting_positions_, { 1, 1 }) + "\n";
143 }
144 
146 {
147  terrain_code& t = (*this)[loc];
148  terrain_code old = t;
149  t = terrain;
150  simplemerge(old, t, mode);
151 
152 }
153 
155 {
156  if(mode == terrain_type_data::OVERLAY) {
157  new_t = t_translation::terrain_code(old_t.base, new_t.overlay);
158  }
159  if(mode == terrain_type_data::BASE) {
160  new_t = t_translation::terrain_code(new_t.base, old_t.overlay);
161  }
162 }
163 
164 void mapgen_gamemap::set_special_location(const std::string& id, const map_location& loc)
165 {
166  bool valid = loc.valid();
167  auto it_left = starting_positions_.left.find(id);
168  if (it_left != starting_positions_.left.end()) {
169  if (valid) {
170  starting_positions_.left.replace_data(it_left, loc);
171  }
172  else {
173  starting_positions_.left.erase(it_left);
174  }
175  }
176  else {
177  starting_positions_.left.insert(it_left, std::make_pair(id, loc));
178  }
179 }
180 
181 map_location mapgen_gamemap::special_location(const std::string& id) const
182 {
183  auto it = starting_positions_.left.find(id);
184  if (it != starting_positions_.left.end()) {
185  auto& coordinate = it->second;
187  }
188  else {
189  return map_location();
190  }
191 }
192 
194 {
195  return luaL_testudata(L, index, terrinmapKey) != nullptr;
196 }
197 
198 
200 {
201  if(luaW_isterrainmap(L, index)) {
202  return static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
203  }
204  return nullptr;
205 }
206 
208 {
209  if(luaW_isterrainmap(L, index)) {
210  return *static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
211  }
212  luaW_type_error(L, index, "terrainmap");
213  throw "luaW_type_error didn't throw";
214 }
215 
217 {
219 }
220 
221 template<typename... T>
223 {
224  mapgen_gamemap* res = new(L) mapgen_gamemap(std::forward<T>(params)...);
226  return res;
227 }
228 
229 /**
230  * Create a map.
231  * - Arg 1: string descripbing the map data.
232  * - or:
233  * - Arg 1: int, width
234  * - Arg 2: int, height
235  * - Arg 3: string, terrain
236 */
238 {
239  if(lua_isnumber(L, 1) && lua_isnumber(L, 2)) {
240  int w = lua_tonumber(L, 1);
241  int h = lua_tonumber(L, 2);
243  luaW_pushmap(L, w, h, terrain);
244  return 1;
245  }
246  else {
247  string_view data_str = luaL_checkstring(L, 1);
248  luaW_pushmap(L, data_str);
249  return 1;
250  }
251 }
252 
253 /**
254  * Destroys a map object before it is collected (__gc metamethod).
255  */
257 {
258  mapgen_gamemap *u = static_cast<mapgen_gamemap*>(lua_touserdata(L, 1));
259  u->mapgen_gamemap::~mapgen_gamemap();
260  return 0;
261 }
262 
263 /**
264  * Gets some data on a map (__index metamethod).
265  * - Arg 1: full userdata containing the map.
266  * - Arg 2: string containing the name of the property.
267  * - Ret 1: something containing the attribute.
268  */
270 {
272  char const *m = luaL_checkstring(L, 2);
273 
274  // Find the corresponding attribute.
275  return_int_attrib("width", tm.total_width());
276  return_int_attrib("height", tm.total_height());
277  return_string_attrib("data", tm.to_string());
278 
279  if(strcmp(m, "special_locations") == 0) {
280  luaW_pushslocs(L, 1);
281  return 1;
282  }
283  if(luaW_getmetafield(L, 1, m)) {
284  return 1;
285  }
286  return 0;
287 }
288 
289 /**
290  * Sets some data on a map (__newindex metamethod).
291  * - Arg 1: full userdata containing the map.
292  * - Arg 2: string containing the name of the property.
293  * - Arg 3: something containing the attribute.
294  */
296 {
298  UNUSED(tm);
299  char const *m = luaL_checkstring(L, 2);
300  std::string err_msg = "unknown modifiable property of map: ";
301  err_msg += m;
302  return luaL_argerror(L, 2, err_msg.c_str());
303 }
304 
305 
306 /**
307  * Sets a terrain code.
308  * - Arg 1: map location.
309  * - Arg 2: terrain code string.
310  * - Arg 3: layer: (overlay|base|both, default=both)
311 */
313 {
315  map_location loc = luaW_checklocation(L, 2);
316  string_view t_str = luaL_checkstring(L, 3);
317 
319  auto mode = terrain_type_data::BOTH;
320 
321  if(!lua_isnoneornil(L, 4)) {
322  string_view mode_str = luaL_checkstring(L, 4);
323  if(mode_str == "base") {
325  }
326  else if(mode_str == "overlay") {
328  }
329  }
330 
331  tm.set_terrain(loc, terrain, mode);
332  return 0;
333 }
334 
335 /**
336  * Gets a terrain code.
337  * - Arg 1: map location.
338  * - Ret 1: string.
339  */
341 {
343  map_location loc = luaW_checklocation(L, 2);
344 
345  auto t = tm[loc];
347  return 1;
348 }
349 
350 static std::vector<gamemap::overlay_rule> read_rules_vector(lua_State *L, int index)
351 {
352  std::vector<gamemap::overlay_rule> rules;
353  for (int i = 1, i_end = lua_rawlen(L, index); i <= i_end; ++i)
354  {
355  lua_rawgeti(L, index, i);
356  if(!lua_istable(L, -1)) {
357  luaL_argerror(L, index, "rules must be a table of tables");
358  }
359  rules.push_back(gamemap::overlay_rule());
360  auto& rule = rules.back();
361  if(luaW_tableget(L, -1, "old")) {
362  rule.old_ = t_translation::read_list(luaW_tostring(L, -1));
363  lua_pop(L, 1);
364  }
365  if(luaW_tableget(L, -1, "new")) {
366  rule.new_ = t_translation::read_list(luaW_tostring(L, -1));
367  lua_pop(L, 1);
368  }
369 
370  if(luaW_tableget(L, -1, "mode")) {
371  auto str = luaW_tostring(L, -1);
372  rule.mode_ = str == "base" ? terrain_type_data::BASE : (str == "overlay" ? terrain_type_data::OVERLAY : terrain_type_data::BOTH);
373  lua_pop(L, 1);
374  }
375 
376  if(luaW_tableget(L, -1, "terrain")) {
378  if(!terrain.empty()) {
379  rule.terrain_ = terrain[0];
380  }
381  lua_pop(L, 1);
382  }
383 
384  if(luaW_tableget(L, -1, "use_old")) {
385  rule.use_old_ = luaW_toboolean(L, -1);
386  lua_pop(L, 1);
387  }
388 
389  if(luaW_tableget(L, -1, "replace_if_failed")) {
390  rule.replace_if_failed_ = luaW_toboolean(L, -1);
391  lua_pop(L, 1);
392  }
393 
394  lua_pop(L, 1);
395  }
396  return rules;
397 }
398 /**
399  * Reaplces part of the map.
400  * - Arg 1: map location.
401  * - Arg 2: map data string.
402  * - Arg 3: table for optional named arguments
403  * - is_odd: boolen, if Arg2 has the odd mapo format (as if it was cut from a odd map location)
404  * - ignore_special_locations: boolean
405  * - rules: table of tables
406 */
408 {
410  map_location loc = luaW_checklocation(L, 2);
412 
413  bool is_odd = false;
414  bool ignore_special_locations = false;
415  std::vector<gamemap::overlay_rule> rules;
416 
417  if(lua_istable(L, 4)) {
418  is_odd = luaW_table_get_def<bool>(L, 4, "is_odd", false);
419  ignore_special_locations = luaW_table_get_def<bool>(L, 4, "ignore_special_locations", false);
420 
421  if(luaW_tableget(L, 4, "rules")) {
422  if(!lua_istable(L, -1)) {
423  return luaL_argerror(L, 4, "rules must be a table");
424  }
425  rules = read_rules_vector(L, -1);
426  lua_pop(L, 1);
427  }
428  }
429 
431  tm1.tiles_,
433  tm2.tiles_,
435  [&](const map_location& loc, const t_translation::terrain_code& t, terrain_type_data::merge_mode mode, bool) { tm1.set_terrain(loc, t, mode); },
436  loc,
437  rules,
438  is_odd,
439  ignore_special_locations
440  );
441 
442  return 0;
443 }
444 
445 namespace lua_terrainmap {
447  {
448  std::ostringstream cmd_out;
449 
450  cmd_out << "Adding terrainmamap metatable...\n";
451 
454  lua_setfield(L, -2, "__gc");
456  lua_setfield(L, -2, "__index");
458  lua_setfield(L, -2, "__newindex");
459  lua_pushstring(L, "terainmap");
460  lua_setfield(L, -2, "__metatable");
461  // terainmap methods
463  lua_setfield(L, -2, "set_terrain");
465  lua_setfield(L, -2, "get_terrain");
467  lua_setfield(L, -2, "get_locations");
469  lua_setfield(L, -2, "get_tiles_radius");
471  lua_setfield(L, -2, "terrain_mask");
472 
473  cmd_out << "Adding terrainmamap2 metatable...\n";
474 
477  lua_setfield(L, -2, "__index");
479  lua_setfield(L, -2, "__newindex");
480  lua_pushstring(L, "special_locations");
481  lua_setfield(L, -2, "__metatable");
482 
483  return cmd_out.str();
484  }
485 }
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:966
#define lua_isnoneornil(L, n)
Definition: lua.h:359
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:684
#define lua_pushcfunction(L, f)
Definition: lua.h:350
utils::string_view luaW_tostring(lua_State *L, int index)
Definition: lua_common.cpp:978
mapgen_gamemap * luaW_toslocs(lua_State *L, int index)
bool is_odd(T num)
Definition: math.hpp:34
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:658
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:817
#define lua_remove(L, idx)
Definition: lua.h:371
bool luaW_isterrainmap(lua_State *L, int index)
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:217
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
int impl_slocs_get(lua_State *L)
t_translation::ter_map tiles_
#define h
#define lua_tonumber(L, i)
Definition: lua.h:341
int h() const
Effective map height.
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:312
basic_string_view< char, std::char_traits< char > > string_view
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:722
void set_special_location(const std::string &id, const map_location &loc)
#define lua_pop(L, n)
Definition: lua.h:344
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:164
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:226
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:890
int intf_mg_get_tiles_radius(lua_State *L)
mapgen_gamemap(utils::string_view data)
map_location special_location(const std::string &id) const
starting_positions starting_positions_
ter_map read_game_map(utils::string_view str, starting_positions &starting_positions, coordinate border_offset)
Reads a gamemap string into a 2D vector.
bool valid() const
Definition: location.hpp:93
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:216
std::string register_metatables(lua_State *L)
LUALIB_API void * luaL_testudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:318
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
static lg::log_domain log_scripting_lua("scripting/lua")
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:450
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:42
#define UNUSED(x)
Definition: global.hpp:34
LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:299
LUA_API void * lua_touserdata(lua_State *L, int idx)
Definition: lapi.cpp:413
std::size_t i
Definition: function.cpp:933
static map_location::DIRECTION s
terrain_code read_terrain_code(utils::string_view str, const ter_layer filler)
Reads a single terrain from a string.
int w() const
Effective map width.
int w
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:237
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:276
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.
LUA_API size_t lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:392
mapgen_gamemap * luaW_pushmap(lua_State *L, T &&... params)
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:353
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:662
static int impl_terainmap_set(lua_State *L)
Sets some data on a map (__newindex metamethod).
void lua_terrainmap_setmetatable(lua_State *L)
ter_list read_list(utils::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
Standard logging facilities (interface).
std::vector< terrain_code > ter_list
Definition: translation.hpp:78
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:491
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:777
static const char terrinmapKey[]
static int impl_terainmap_get(lua_State *L)
Gets some data on a map (__index metamethod).
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124