Symphony Of Empires
lua_api.cpp
Go to the documentation of this file.
1 // Symphony of Empires
2 // Copyright (C) 2021, Symphony of Empires contributors
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 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // server/lua_api.cpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 // NOTE: Use .at() instead of the [] operator since it will
26 // throw exceptions when an invalid element is accessed so
27 // the lua exceptions
28 
29 #include <cstring>
30 #include <cstdlib>
31 #include <cassert>
32 
33 #include "eng3d/utils.hpp"
34 #include "eng3d/log.hpp"
35 
36 #include "server/lua_api.hpp"
37 #include "world.hpp"
38 #include "nation.hpp"
39 #include "server/economy.hpp"
40 #include "event.hpp"
41 #include "building.hpp"
42 
43 int LuaAPI::register_new_table(lua_State* L, const std::string& name, const std::vector<luaL_Reg> meta, const std::vector<luaL_Reg> methods) {
44  if(luaL_newmetatable(L, name.c_str())) {
45  lua_newtable(L);
46 
47  // Methods
48  for(const auto& reg : methods)
49  lua_register(L, reg.name, reg.func);
50  lua_setfield(L, -2, "__index");
51 
52  // Metatable
53  for(const auto& reg : meta)
54  lua_register(L, reg.name, reg.func);
55 
56  lua_pushstring(L, name.c_str());
57  lua_setfield(L, -2, "__metatable");
58 
59  lua_setglobal(L, name.c_str());
60  }
61  return 0;
62 }
63 
64 // !!! IMPORTANT !!!
65 // Doing changes to the world state (like Nation and Province) does NOT require an explicit update as this is done
66 // after each economical tick
67 // HOWEVER, adding new elements or changing other states REQUIRES a explicit synchronization!!
68 template<typename T>
69 static const T& find_or_throw(const std::string_view ref_name) {
70  const auto& list = World::get_instance().get_list((T*)nullptr);
71  const auto result = std::find_if(list.begin(), list.end(), [ref_name](const auto& o) {
72  return !strcmp(o.ref_name.c_str(), ref_name.data());
73  });
74 
75  if(result == list.end())
76  CXX_THROW(Eng3D::LuaException, translate_format("Object<%s> not found", typeid(T).name()).c_str());
77  return *result;
78 }
79 
80 void append_to_table(lua_State* L, int* index, float number) {
81  lua_pushnumber(L, (*index)++);
82  lua_pushnumber(L, number);
83  lua_settable(L, -3);
84 }
85 
86 void append_to_table(lua_State* L, int index, float number) {
87  append_to_table(L, &index, number);
88 }
89 
90 void append_to_table(lua_State* L, int* index, const std::string& text) {
91  lua_pushnumber(L, (*index)++);
92  lua_pushstring(L, text.c_str());
93  lua_settable(L, -3);
94 }
95 
96 void append_to_table(lua_State* L, int index, const std::string& text) {
97  append_to_table(L, &index, text);
98 }
99 
100 static float pop_number(lua_State* L) {
101  const float amount = lua_tonumber(L, -1);
102  lua_pop(L, 1);
103  return amount;
104 }
105 
106 static std::string pop_string(lua_State* L) {
107  const std::string& text = luaL_checkstring(L, -1);
108  if(text.empty())
109  luaL_error(L, "Expected a text but got empty string");
110  lua_pop(L, 1);
111  return text;
112 }
113 
114 int LuaAPI::add_terrain_type(lua_State* L) {
116  luaL_error(L, "MP-Sync in this function is not supported");
117 
118  TerrainType terrain_type{};
119  terrain_type.ref_name = luaL_checkstring(L, 1);
120  terrain_type.name = luaL_checkstring(L, 2);
121  terrain_type.color = std::byteswap<std::uint32_t>(static_cast<int>(lua_tonumber(L, 3))) >> 8;
122  terrain_type.color |= 0xff000000;
123  terrain_type.penalty = lua_tonumber(L, 4);
124  terrain_type.is_water_body = lua_toboolean(L, 5);
125  g_world.insert(terrain_type);
126  lua_pushnumber(L, g_world.terrain_types.size() - 1);
127  return 1;
128 }
129 
131  const auto& terrain_type = g_world.terrain_types.at(lua_tonumber(L, 1));
132 
133  lua_pushstring(L, terrain_type.ref_name.c_str());
134  lua_pushstring(L, terrain_type.name.c_str());
135  lua_pushnumber(L, std::byteswap<std::uint32_t>((terrain_type.color & 0x00ffffff) << 8));
136  lua_pushnumber(L, terrain_type.penalty);
137  lua_pushboolean(L, terrain_type.is_water_body);
138  return 5;
139 }
140 
141 int LuaAPI::get_terrain_type(lua_State* L) {
142  const auto& terrain_type = find_or_throw<TerrainType>(luaL_checkstring(L, 1));
143  lua_pushnumber(L, (size_t)g_world.get_id(terrain_type));
144  lua_pushstring(L, terrain_type.name.c_str());
145  lua_pushnumber(L, std::byteswap<std::uint32_t>((terrain_type.color & 0x00ffffff) << 8));
146  lua_pushnumber(L, terrain_type.penalty);
147  lua_pushboolean(L, terrain_type.is_water_body);
148  return 5;
149 }
150 
151 int LuaAPI::add_technology(lua_State* L) {
153  luaL_error(L, "MP-Sync in this function is not supported");
154 
155  Technology technology{};
156  technology.ref_name = luaL_checkstring(L, 1);
157  technology.name = luaL_checkstring(L, 2);
158  technology.description = lua_tostring(L, 3);
159  technology.cost = (lua_tonumber(L, 4));
160  technology.type = (TechnologyType)((int)lua_tonumber(L, 5));
161  g_world.insert(technology);
162  lua_pushnumber(L, g_world.technologies.size() - 1);
163  return 1;
164 }
165 
166 int LuaAPI::get_technology(lua_State* L) {
167  const auto& technology = find_or_throw<Technology>(luaL_checkstring(L, 1));
168  lua_pushnumber(L, (size_t)g_world.get_id(technology));
169  lua_pushstring(L, technology.name.c_str());
170  lua_pushstring(L, technology.description.c_str());
171  lua_pushnumber(L, technology.cost);
172  lua_pushnumber(L, technology.type);
173  return 5;
174 }
175 
177  Technology* technology = &g_world.technologies.at(lua_tonumber(L, 1));
178  technology->req_technologies.push_back(TechnologyId(lua_tonumber(L, 2)));
179  return 0;
180 }
181 
182 int LuaAPI::add_building_type(lua_State* L) {
184  luaL_error(L, "MP-Sync in this function is not supported");
185 
186  BuildingType building_type{};
187  building_type.ref_name = luaL_checkstring(L, 1);
188  building_type.name = luaL_checkstring(L, 2);
189  building_type.can_plot_on_sea(lua_toboolean(L, 3));
190  building_type.can_build_land_units(lua_toboolean(L, 4));
191  building_type.can_build_naval_units(lua_toboolean(L, 5));
192  g_world.insert(building_type);
193  lua_pushnumber(L, g_world.building_types.size() - 1);
194  return 1;
195 }
196 
197 int LuaAPI::get_building_type(lua_State* L) {
198  const auto& building_type = find_or_throw<BuildingType>(luaL_checkstring(L, 1));
199 
200  lua_pushnumber(L, (size_t)building_type.get_id());
201  lua_pushstring(L, building_type.ref_name.c_str());
202  lua_pushstring(L, building_type.name.c_str());
203  lua_pushboolean(L, building_type.can_plot_on_sea());
204  lua_pushboolean(L, building_type.can_build_land_units());
205  lua_pushboolean(L, building_type.can_build_naval_units());
206  lua_pushnumber(L, 0.f);
207  return 7;
208 }
209 
210 int LuaAPI::add_good(lua_State* L) {
212  luaL_error(L, "MP-Sync in this function is not supported");
213 
214  Commodity commodity{};
215  commodity.ref_name = luaL_checkstring(L, 1);
216  commodity.name = luaL_checkstring(L, 2);
217  g_world.insert(commodity);
218  lua_pushnumber(L, g_world.commodities.size() - 1);
219  return 1;
220 }
221 
222 int LuaAPI::get_good(lua_State* L) {
223  const auto& commodity = find_or_throw<Commodity>(luaL_checkstring(L, 1));
224  lua_pushnumber(L, (size_t)g_world.get_id(commodity));
225  lua_pushstring(L, commodity.name.c_str());
226  return 2;
227 }
228 
230  auto& industry_type = g_world.building_types.at(lua_tonumber(L, 1));
231  auto& commodity = g_world.commodities.at(lua_tonumber(L, 2));
232  industry_type.input_ids.push_back(commodity);
233  industry_type.num_req_workers += 100;
234  return 0;
235 }
236 
238  auto& industry_type = g_world.building_types.at(lua_tonumber(L, 1));
239  auto& commodity = g_world.commodities.at(lua_tonumber(L, 2));
240  industry_type.output_id = commodity;
241  industry_type.num_req_workers += 100;
242  return 0;
243 }
244 
246  auto& industry_type = g_world.building_types.at(lua_tonumber(L, 1));
247  auto& commodity = g_world.commodities.at(lua_tonumber(L, 2));
248  industry_type.req_goods.emplace_back(commodity, lua_tonumber(L, 3));
249  return 0;
250 }
251 
253  auto& industry_type = g_world.building_types.at(lua_tonumber(L, 1));
254  auto& technology = g_world.technologies.at(lua_tonumber(L, 2));
255  industry_type.req_technologies.push_back(technology);
256  return 0;
257 }
258 
259 int LuaAPI::add_nation(lua_State* L) {
261  luaL_error(L, "MP-Sync in this function is not supported");
262 
263  Nation nation{};
264  nation.ref_name = luaL_checkstring(L, 1);
265  nation.name = luaL_checkstring(L, 2);
266  nation.ideology_id = IdeologyId(0);
267  nation.commodity_production.resize(g_world.commodities.size(), 1.f);
268  nation.religion_discrim.resize(g_world.religions.size(), 0.f);
269  nation.language_acceptance.resize(g_world.languages.size(), 0.f);
270  nation.client_hints.resize(g_world.ideologies.size());
271  nation.research.resize(g_world.technologies.size());
272 
273  // Check for duplicates
274  for(const auto& other_nation : g_world.nations) {
275  if(nation.ref_name == other_nation.ref_name)
276  luaL_error(L, string_format("Duplicate ref_name %s", nation.ref_name.c_str()).c_str());
277  }
278  g_world.insert(nation);
279  lua_pushnumber(L, g_world.nations.size() - 1);
280  return 1;
281 }
282 
283 int LuaAPI::get_nation(lua_State* L) {
284  const auto& nation = find_or_throw<Nation>(luaL_checkstring(L, 1));
285  lua_pushnumber(L, (size_t)g_world.get_id(nation));
286  lua_pushstring(L, nation.name.c_str());
287  return 2;
288 }
289 
290 int LuaAPI::get_nation_by_id(lua_State* L) {
291  const auto& nation = g_world.nations.at(lua_tonumber(L, 1));
292  lua_pushstring(L, nation.name.c_str());
293  lua_pushstring(L, nation.ref_name.c_str());
294  return 2;
295 }
296 
297 int LuaAPI::get_all_nations(lua_State* L) {
298  lua_newtable(L);
299 
300  size_t i = 0;
301  for(const auto& nation : g_world.nations) {
302  lua_pushnumber(L, nation);
303  lua_rawseti(L, -2, i + 1);
304  ++i;
305  }
306  return 1;
307 }
308 
309 int LuaAPI::switch_nation_soul(lua_State* L) {
310  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
311  auto& target = g_world.nations.at(lua_tonumber(L, 2));
312  if(&nation == &target)
313  luaL_error(L, string_format("%s can't switch to itself", nation.ref_name.c_str()).c_str());
315  return 0;
316 }
317 
319  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
320  auto& other_nation = g_world.nations.at(lua_tonumber(L, 2));
321  if(&nation == &other_nation)
322  luaL_error(L, string_format("%s can't declare war on itself", nation.ref_name.c_str()).c_str());
323  nation.declare_war(other_nation);
324  return 0;
325 }
326 
328  const auto& nation = g_world.nations.at(lua_tonumber(L, 1));
329  lua_newtable(L);
330 
331  size_t i = 0;
332  for(const auto province_id : nation.owned_provinces) {
333  lua_pushnumber(L, (size_t)province_id);
334  lua_rawseti(L, -2, i + 1);
335  ++i;
336  }
337  return 1;
338 }
339 
341  const auto& nation = g_world.nations.at(lua_tonumber(L, 1));
342  lua_newtable(L);
343 
344  size_t i = 0;
345  for(const auto& province : g_world.provinces) {
346  bool is_nuclei = false;
347  for(const auto& nucleus_id : province.nuclei) {
348  auto& nucleus = g_world.nations[nucleus_id];
349  if(&nucleus == &nation) {
350  is_nuclei = true;
351  break;
352  }
353  }
354  if(!is_nuclei) continue;
355  lua_pushnumber(L, (size_t)g_world.get_id(province));
356  lua_rawseti(L, -2, i + 1);
357  ++i;
358  }
359  return 1;
360 }
361 
363  return 0;
364 }
365 
366 int LuaAPI::set_nation_capital(lua_State* L) {
367  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
368  nation.capital_id = ProvinceId(lua_tonumber(L, 2));
369  return 0;
370 }
371 
373  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
374  nation.language_acceptance.at(lua_tonumber(L, 2)) = 1.f;
375  return 0;
376 }
377 
379  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
380  nation.religion_discrim.at(lua_tonumber(L, 2)) = 1.f;
381  return 0;
382 }
383 
385  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
386  Nation::ClientHint hint{};
387  hint.ideology_id = IdeologyId(lua_tonumber(L, 2));
388  hint.name = luaL_checkstring(L, 3);
389  hint.color = std::byteswap<std::uint32_t>(static_cast<int>(lua_tonumber(L, 4))) >> 8;
390  hint.color |= 0xff000000;
391  nation.client_hints[hint.ideology_id] = hint;
392  return 0;
393 }
394 
396  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
397  auto& other_nation = g_world.nations.at(lua_tonumber(L, 2));
398  auto& relation = g_world.get_relation(nation, other_nation);
399  lua_pushnumber(L, relation.alliance);
400  lua_pushnumber(L, relation.relation);
401  lua_pushboolean(L, relation.has_war);
402  return 3;
403 }
404 
406  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
407  auto& other_nation = g_world.nations.at(lua_tonumber(L, 2));
408  auto& relation = g_world.get_relation(nation, other_nation);
409  relation.alliance = lua_tonumber(L, 3);
410  relation.relation = lua_tonumber(L, 4);
411  relation.has_war = lua_toboolean(L, 5);
412  return 0;
413 }
414 
416  auto& nation = g_world.nations.at(lua_tonumber(L, 1));
417  auto& other_nation = g_world.nations.at(lua_tonumber(L, 2));
418  if(!g_world.get_relation(nation, other_nation).has_war)
419  nation.declare_war(other_nation);
420  return 0;
421 }
422 
423 int LuaAPI::add_province(lua_State* L) {
425  luaL_error(L, "MP-Sync in this function is not supported");
426 
427  Province province{};
428  province.ref_name = luaL_checkstring(L, 1);
429  province.color = (std::byteswap<std::uint32_t>(static_cast<int>(lua_tonumber(L, 2))) >> 8) | 0xff000000;
430  province.name = luaL_checkstring(L, 3);
431  province.terrain_type_id = TerrainTypeId(lua_tonumber(L, 4));
432 
433  // Load rgo_size
434  province.rgo_size.resize(g_world.commodities.size(), 0);
435  luaL_checktype(L, 5, LUA_TTABLE);
436  size_t n_rgo_size = lua_rawlen(L, 5);
437  for(size_t i = 1; i <= n_rgo_size; i++) {
438  lua_rawgeti(L, 5, i);
439  if(!lua_istable(L, -1))
440  luaL_error(L, "RGO size is a multidimensional table, \'rgo_size={\"wheat\",1}\' is not valid, do \'rgo_size={{\"wheat\",1}}\' instead");
441 
442  {
443  lua_rawgeti(L, -1, 1);
444  const Commodity& commodity = find_or_throw<Commodity>(luaL_checkstring(L, -1));
445  lua_pop(L, 1);
446 
447  lua_rawgeti(L, -1, 2);
448  const uint32_t amount = (uint32_t)lua_tonumber(L, -1);
449  lua_pop(L, 1);
450 
451  province.rgo_size[commodity] = amount;
452  }
453  lua_pop(L, 1);
454  }
455 
456  // Check for duplicates
457  for(size_t i = 0; i < g_world.provinces.size(); i++) {
458  if(province.color == g_world.provinces[i].color) {
459  luaL_error(L, string_format("%s province has same color as %s", province.ref_name.c_str(), g_world.provinces[i].ref_name.c_str()).c_str());
460  } else if(province.ref_name == g_world.provinces[i].ref_name) {
461  luaL_error(L, string_format("Duplicate ref_name %s", province.ref_name.c_str()).c_str());
462  }
463  }
464 
465  province.products.resize(g_world.commodities.size(), Product{});
466  province.languages.resize(g_world.languages.size(), 0.f);
467  province.religions.resize(g_world.religions.size(), 0.f);
468  province.buildings.resize(g_world.building_types.size());
469  for(auto& building : province.buildings)
470  building.estate_foreign.resize(g_world.nations.size());
471 
472  {
473  size_t i = 0;
474  for(auto& pop : province.pops) {
475  pop.type_id = PopTypeId(i);
476  i++;
477  }
478  }
479 
480  // Set bounding box of province to the whole world (will later be resized at the bitmap-processing step)
481  province.box_area = Eng3D::Rect(0, 0, std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max());
482  g_world.insert(province);
483  lua_pushnumber(L, g_world.provinces.size() - 1);
484  return 1;
485 }
486 
487 int LuaAPI::update_province(lua_State* L) {
488  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
489  province.ref_name = luaL_checkstring(L, 2);
490  province.color = (std::byteswap<std::uint32_t>(static_cast<int>(lua_tonumber(L, 3))) >> 8) | 0xff000000;
491  province.name = luaL_checkstring(L, 4);
492  province.terrain_type_id = TerrainTypeId(lua_tonumber(L, 5));
493  // Check for duplicates
494  for(size_t i = 0; i < g_world.provinces.size(); i++) {
495  if(province.color == g_world.provinces[i].color) {
496  luaL_error(L, string_format("%s province has same color as %s", province.ref_name.c_str(), g_world.provinces[i].ref_name.c_str()).c_str());
497  } else if(province.ref_name == g_world.provinces[i].ref_name) {
498  luaL_error(L, string_format("Duplicate ref_name %s", province.ref_name.c_str()).c_str());
499  }
500  }
501  return 0;
502 }
503 
504 int LuaAPI::get_province(lua_State* L) {
505  const Province& province = find_or_throw<Province>(luaL_checkstring(L, 1));
506  lua_pushnumber(L, (size_t)g_world.get_id(province));
507  lua_pushstring(L, province.name.c_str());
508  lua_pushnumber(L, std::byteswap<std::uint32_t>((province.color & 0x00ffffff) << 8));
509  lua_pushnumber(L, (size_t)province.terrain_type_id);
510  lua_newtable(L);
511  size_t index = 1;
512  for(size_t i = 0; i < province.rgo_size.size(); i++) {
513  if(province.rgo_size[i] != 0) {
514  lua_pushnumber(L, index++);
515  lua_newtable(L);
516  append_to_table(L, 1, g_world.commodities[i].ref_name.c_str());
517  append_to_table(L, 2, province.rgo_size[i]);
518  lua_settable(L, -3);
519  }
520  }
521  return 5;
522 }
523 
524 int LuaAPI::get_province_by_id(lua_State* L) {
525  const auto& province = g_world.provinces.at(lua_tonumber(L, 1));
526  lua_pushstring(L, province.ref_name.c_str());
527  lua_pushstring(L, province.name.c_str());
528  lua_pushnumber(L, std::byteswap<std::uint32_t>((province.color & 0x00ffffff) << 8));
529  lua_pushnumber(L, (size_t)province.terrain_type_id);
530  lua_newtable(L);
531  size_t index = 1;
532  for(size_t i = 0; i < province.rgo_size.size(); i++) {
533  if(province.rgo_size[i] != 0) {
534  lua_pushnumber(L, index++);
535  lua_newtable(L);
536  append_to_table(L, 1, g_world.commodities[i].ref_name.c_str());
537  append_to_table(L, 2, province.rgo_size[i]);
538  lua_settable(L, -3);
539  }
540  }
541  return 5;
542 }
543 
544 int LuaAPI::province_add_unit(lua_State* L) {
545  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
546  auto& unit_type = g_world.unit_types.at(lua_tonumber(L, 2));
547  const size_t size = lua_tonumber(L, 3);
548 
549  Unit unit{};
550  unit.set_owner(g_world.nations.at(province.owner_id));
551  unit.type_id = unit_type;
552  unit.experience = 1.f;
553  unit.size = size;
554  unit.base = unit_type.max_health;
555  g_world.unit_manager.add_unit(unit, province);
556  return 0;
557 }
558 
561  luaL_error(L, "MP-Sync in this function is not supported");
562  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
563  const auto& building_type = g_world.building_types.at(lua_tonumber(L, 2)); // Add up a level of upgrade
564  province.buildings[building_type].level = lua_tonumber(L, 3);
565  province.buildings[building_type].budget += 1000.f;
566  return 0;
567 }
568 
569 int LuaAPI::give_province_to(lua_State* L) {
570  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
571  g_world.nations.at(lua_tonumber(L, 2)).give_province(province);
572  return 0;
573 }
574 
576  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
577  auto& nation = g_world.nations.at(lua_tonumber(L, 2));
578  const auto& unit_ids = g_world.unit_manager.get_province_units(province);
579  for(const auto unit_id : unit_ids) {
580  auto& unit = g_world.unit_manager.units[unit_id];
581  if(unit.owner_id == province.controller_id)
582  unit.set_owner(nation);
583  }
584  nation.control_province(province);
585  nation.give_province(province);
586 
587  // Take all the troops of the dead nation if this is the last province of 'em
588  if(!g_world.nations[province.controller_id].exists())
589  g_world.unit_manager.units.for_each([&](auto& unit) {
590  if(unit.owner_id == province.controller_id)
591  unit.set_owner(nation);
592  });
593  return 0;
594 }
595 
596 // Obtains the owner of a province (ref_name)
597 int LuaAPI::get_province_owner(lua_State* L) {
598  const auto& province = g_world.provinces.at(lua_tonumber(L, 1));
599  lua_pushstring(L, g_world.nations[province.controller_id].ref_name.c_str());
600  return 1;
601 }
602 
603 // Get the country who owms a larger chunk of the province - this is not the same as owner
605  const auto& province = g_world.provinces.at(lua_tonumber(L, 1));
606  lua_pushnumber(L, (size_t)province.controller_id);
607  return 1;
608 }
609 
610 // Obtains the neighbours of a province (by ID)
612  const auto& province = g_world.provinces.at(lua_tonumber(L, 1));
613  lua_newtable(L);
614  size_t i = 0;
615  for(const auto neighbour_id : province.neighbour_ids) {
616  lua_pushnumber(L, (size_t)neighbour_id);
617  lua_rawseti(L, -2, i + 1);
618  ++i;
619  }
620  return 1;
621 }
622 
624  const auto& province = g_world.provinces.at(lua_tonumber(L, 1));
625  lua_newtable(L);
626  size_t i = 0;
627  for(const auto& nucleus_id : province.nuclei) {
628  lua_pushnumber(L, (size_t)nucleus_id);
629  lua_rawseti(L, -2, i + 1);
630  ++i;
631  }
632  return 1;
633 }
634 
635 int LuaAPI::add_province_pop(lua_State* L) {
636  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
637  auto& pop = province.pops.at(lua_tonumber(L, 2));
638  pop.size = lua_tonumber(L, 3);
639  if(!pop.size) {
640  luaL_error(L, "Can't create pops with 0 size");
641  return 0;
642  }
643  pop.literacy = lua_tonumber(L, 4);
644  pop.budget = pop.size;
645  return 0;
646 }
647 
648 int LuaAPI::rename_province(lua_State* L) {
649  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
650  province.name = luaL_checkstring(L, 2);
651  return 0;
652 }
653 
655  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
656  province.nuclei.emplace_back(static_cast<size_t>(lua_tonumber(L, 2)));
657  std::sort(province.nuclei.begin(), province.nuclei.end());
658  auto last = std::unique(province.nuclei.begin(), province.nuclei.end());
659  province.nuclei.erase(last, province.nuclei.end());
660  return 0;
661 }
662 
664  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
665  province.languages.at(lua_tonumber(L, 2)) = lua_tonumber(L, 3);
666  return 0;
667 }
668 
670  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
671  province.religions.at(lua_tonumber(L, 2)) = lua_tonumber(L, 3);
672  return 0;
673 }
674 
675 int LuaAPI::add_province_owner(lua_State* L) {
676  auto& province = g_world.provinces.at(lua_tonumber(L, 1));
677  auto& nation = g_world.nations.at(lua_tonumber(L, 2));
678  nation.give_province(province);
679  return 0;
680 }
681 
682 int LuaAPI::add_event(lua_State* L) {
684  luaL_error(L, "MP-Sync in this function is not supported");
685 
686  Event event{};
687  event.ref_name = luaL_checkstring(L, 1);
688  lua_pushvalue(L, 2);
689  event.conditions_function = luaL_ref(L, LUA_REGISTRYINDEX);
690  lua_pushvalue(L, 3);
691  event.do_event_function = luaL_ref(L, LUA_REGISTRYINDEX);
692  event.title = luaL_checkstring(L, 4);
693  event.text = luaL_checkstring(L, 5);
694  event.checked = lua_toboolean(L, 6);
695  g_world.insert(event);
696  lua_pushnumber(L, (size_t)g_world.get_id(event));
697  return 1;
698 }
699 
700 int LuaAPI::update_event(lua_State* L) {
701  auto& event = g_world.events[lua_tonumber(L, 1)];
702  event.ref_name = luaL_checkstring(L, 2);
703  lua_pushvalue(L, 3);
704  event.conditions_function = luaL_ref(L, LUA_REGISTRYINDEX);
705  lua_pushvalue(L, 4);
706  event.do_event_function = luaL_ref(L, LUA_REGISTRYINDEX);
707  event.title = luaL_checkstring(L, 5);
708  event.text = luaL_checkstring(L, 6);
709  event.checked = lua_toboolean(L, 7);
710  return 0;
711 }
712 
713 int LuaAPI::get_event(lua_State* L) {
714  const auto& event = find_or_throw<Event>(luaL_checkstring(L, 1));
715  lua_pushnumber(L, (size_t)g_world.get_id(event));
717  lua_pushinteger(L, event.conditions_function);
718  lua_pushinteger(L, event.do_event_function);
719  lua_pushstring(L, event.title.c_str());
720  lua_pushstring(L, event.text.c_str());
721  lua_pushboolean(L, event.checked);
722  return 6;
723 }
724 
726  // Add receivers of the event by id
727  auto& event = g_world.events.at(lua_tonumber(L, 1));
728  for(size_t i = 0; i < lua_tonumber(L, 2); i++)
729  event.receiver_ids.push_back((size_t)lua_tonumber(L, 3 + i));
730  return 0;
731 }
732 
733 int LuaAPI::add_decision(lua_State* L) {
734  Event& event = g_world.events.at(lua_tonumber(L, 1));
735  Decision decision = Decision();
736  decision.ref_name = luaL_checkstring(L, 2);
737  decision.name = luaL_checkstring(L, 3);
738  lua_pushvalue(L, 4);
739  decision.do_decision_function = luaL_ref(L, LUA_REGISTRYINDEX);
740  decision.effects = luaL_checkstring(L, 5);
741  event.decisions.push_back(decision); // Add onto vector
742  return 0;
743 }
744 
745 int LuaAPI::add_pop_type(lua_State* L) {
747  luaL_error(L, "MP-Sync in this function is not supported");
748 
749  PopType pop_type{};
750  pop_type.ref_name = luaL_checkstring(L, 1);
751  pop_type.name = luaL_checkstring(L, 2);
752  pop_type.social_value = lua_tonumber(L, 3);
753 
754  pop_type.basic_needs_amount.resize(g_world.commodities.size(), 0.f);
755  pop_type.luxury_needs_satisfaction.resize(g_world.commodities.size(), 0.f);
756  pop_type.luxury_needs_deminishing_factor.resize(g_world.commodities.size(), 0.f);
757 
758  // Lua next = pops top and then pushes key & value in table
759  lua_pushvalue(L, 4);
760  lua_pushnil(L);
761  while(lua_next(L, -2)) {
762  lua_pushnil(L);
763  lua_next(L, -2);
764  const auto& commodity = find_or_throw<Commodity>(pop_string(L));
765  lua_next(L, -2);
766  const float amount = pop_number(L);
767  lua_pop(L, 2);
768  pop_type.basic_needs_amount[commodity] = amount;
769  }
770  lua_pop(L, 1);
771 
772  lua_pushvalue(L, 5);
773  lua_pushnil(L);
774  while(lua_next(L, -2)) {
775  lua_pushnil(L);
776  lua_next(L, -2);
777  const auto& commodity = find_or_throw<Commodity>(pop_string(L));
778  lua_next(L, -2);
779  const float satisfaction = pop_number(L);
780  lua_next(L, -2);
781  const float deminishing = pop_number(L);
782  lua_pop(L, 2);
783  pop_type.luxury_needs_satisfaction[commodity] = satisfaction;
784  pop_type.luxury_needs_deminishing_factor[commodity] = deminishing;
785  }
786  lua_pop(L, 1);
787 
788  g_world.insert(pop_type);
789  lua_pushnumber(L, g_world.pop_types.size() - 1);
790  return 1;
791 }
792 
793 int LuaAPI::get_pop_type(lua_State* L) {
794  const auto& pop_type = find_or_throw<PopType>(luaL_checkstring(L, 1));
795 
796  lua_pushnumber(L, (size_t)pop_type.get_id());
797  lua_pushstring(L, pop_type.name.c_str());
798  lua_pushnumber(L, pop_type.social_value);
799  lua_newtable(L);
800  size_t index = 1;
801  for(size_t i = 0; i < pop_type.basic_needs_amount.size(); i++) {
802  if(pop_type.basic_needs_amount[i] != 0) {
803  lua_pushnumber(L, index++);
804  lua_newtable(L);
805  append_to_table(L, 1, g_world.commodities[i].ref_name.c_str());
806  append_to_table(L, 2, pop_type.basic_needs_amount[i]);
807  lua_settable(L, -3);
808  }
809  }
810  lua_newtable(L);
811  index = 1;
812  for(size_t i = 0; i < pop_type.luxury_needs_satisfaction.size(); i++) {
813  if(pop_type.luxury_needs_satisfaction[i] != 0) {
814  lua_pushnumber(L, index++);
815  lua_newtable(L);
816  append_to_table(L, 1, g_world.commodities[i].ref_name.c_str());
817  append_to_table(L, 2, pop_type.luxury_needs_satisfaction[i]);
818  append_to_table(L, 3, pop_type.luxury_needs_deminishing_factor[i]);
819  lua_settable(L, -3);
820  }
821  }
822  return 5;
823 }
824 
825 int LuaAPI::get_pop_type_by_id(lua_State* L) {
826  const PopType& pop_type = g_world.pop_types.at(lua_tonumber(L, 1));
827  lua_pushstring(L, pop_type.ref_name.c_str());
828  lua_pushstring(L, pop_type.name.c_str());
829  lua_pushnumber(L, pop_type.social_value);
830  lua_newtable(L);
831  size_t index = 1;
832  for(size_t i = 0; i < pop_type.basic_needs_amount.size(); i++) {
833  if(pop_type.basic_needs_amount[i] != 0) {
834  lua_pushnumber(L, index++);
835  lua_newtable(L);
836  append_to_table(L, 1, g_world.commodities[i].ref_name.c_str());
837  append_to_table(L, 2, pop_type.basic_needs_amount[i]);
838  lua_settable(L, -3);
839  }
840  }
841  index = 1;
842  for(size_t i = 0; i < pop_type.luxury_needs_satisfaction.size(); i++) {
843  if(pop_type.luxury_needs_satisfaction[i] != 0) {
844  lua_pushnumber(L, index++);
845  lua_newtable(L);
846  append_to_table(L, 1, g_world.commodities[i].ref_name.c_str());
849  lua_settable(L, -3);
850  }
851  }
852  return 5;
853 }
854 
855 int LuaAPI::add_language(lua_State* L) {
857  luaL_error(L, "MP-Sync in this function is not supported");
858 
859  Language language{};
860  language.ref_name = luaL_checkstring(L, 1);
861  language.name = luaL_checkstring(L, 2);
862  language.color = (std::byteswap<std::uint32_t>(static_cast<int>(lua_tonumber(L, 3))) >> 8) | 0xff000000;
863  language.adjective = luaL_checkstring(L, 4);
864  language.noun = luaL_checkstring(L, 5);
865  language.combo_form = luaL_checkstring(L, 6);
866  g_world.insert(language);
867  lua_pushnumber(L, g_world.languages.size() - 1);
868  return 1;
869 }
870 
871 int LuaAPI::get_language(lua_State* L) {
872  const auto& language = find_or_throw<Language>(luaL_checkstring(L, 1));
873 
874  lua_pushnumber(L, (size_t)g_world.get_id(language));
875  lua_pushstring(L, language.name.c_str());
876  lua_pushnumber(L, std::byteswap<std::uint32_t>((language.color & 0x00ffffff) << 8));
877  lua_pushstring(L, language.adjective.c_str());
878  lua_pushstring(L, language.noun.c_str());
879  lua_pushstring(L, language.combo_form.c_str());
880  return 6;
881 }
882 
883 int LuaAPI::get_language_by_id(lua_State* L) {
884  const auto& language = g_world.languages.at(lua_tonumber(L, 1));
885  lua_pushstring(L, language.ref_name.c_str());
886  lua_pushstring(L, language.name.c_str());
887  lua_pushnumber(L, std::byteswap<std::uint32_t>((language.color & 0x00ffffff) << 8));
888  lua_pushstring(L, language.adjective.c_str());
889  lua_pushstring(L, language.noun.c_str());
890  lua_pushstring(L, language.combo_form.c_str());
891  return 6;
892 }
893 
894 int LuaAPI::add_religion(lua_State* L) {
896  luaL_error(L, "MP-Sync in this function is not supported");
897 
898  Religion religion{};
899  religion.ref_name = luaL_checkstring(L, 1);
900  religion.name = luaL_checkstring(L, 2);
901  religion.color = (std::byteswap<std::uint32_t>(static_cast<int>(lua_tonumber(L, 3))) >> 8) | 0xff000000;
902  g_world.insert(religion);
903  lua_pushnumber(L, g_world.religions.size() - 1);
904  return 1;
905 }
906 
907 int LuaAPI::get_religion(lua_State* L) {
908  const auto& religion = find_or_throw<Religion>(luaL_checkstring(L, 1));
909  lua_pushnumber(L, (size_t)g_world.get_id(religion));
910  lua_pushstring(L, religion.name.c_str());
911  lua_pushnumber(L, std::byteswap<std::uint32_t>((religion.color & 0x00ffffff) << 8));
912  return 3;
913 }
914 
915 int LuaAPI::get_religion_by_id(lua_State* L) {
916  const auto& religion = g_world.religions.at(lua_tonumber(L, 1));
917  lua_pushstring(L, religion.ref_name.c_str());
918  lua_pushstring(L, religion.name.c_str());
919  lua_pushnumber(L, std::byteswap<std::uint32_t>((religion.color & 0x00ffffff) << 8));
920  return 3;
921 }
922 
923 int LuaAPI::add_unit_type(lua_State* L) {
925  luaL_error(L, "MP-Sync in this function is not supported");
926 
927  UnitType unit_type{};
928  unit_type.ref_name = luaL_checkstring(L, 1);
929  unit_type.name = luaL_checkstring(L, 2);
930  unit_type.attack = (lua_tonumber(L, 3));
931  unit_type.defense = (lua_tonumber(L, 4));
932  unit_type.max_health = (lua_tonumber(L, 5));
933  unit_type.is_ground = lua_toboolean(L, 6);
934  unit_type.is_naval = lua_toboolean(L, 7);
935  unit_type.speed = (lua_tonumber(L, 8));
936  g_world.insert(unit_type);
937  lua_pushnumber(L, g_world.unit_types.size() - 1);
938  return 1;
939 }
940 
941 int LuaAPI::get_unit_type(lua_State* L) {
942  const auto unit_type = find_or_throw<UnitType>(luaL_checkstring(L, 1));
943 
944  lua_pushnumber(L, (size_t)g_world.get_id(unit_type));
945  lua_pushstring(L, unit_type.name.c_str());
946  lua_pushnumber(L, unit_type.attack);
947  lua_pushnumber(L, unit_type.defense);
948  lua_pushnumber(L, unit_type.max_health);
949  lua_pushboolean(L, unit_type.is_ground);
950  lua_pushboolean(L, unit_type.is_naval);
951  lua_pushnumber(L, unit_type.speed);
952  return 8;
953 }
954 
956  auto& unit_type = g_world.unit_types.at(lua_tonumber(L, 1));
957  auto& commodity = g_world.commodities.at(lua_tonumber(L, 2));
958  size_t amount = lua_tonumber(L, 3);
959  unit_type.req_goods.emplace_back(commodity, amount);
960  return 0;
961 }
962 
963 static int traceback(lua_State* L) {
964  lua_getglobal(L, "debug");
965  lua_getfield(L, -1, "traceback");
966  lua_pushvalue(L, 1);
967  lua_pushinteger(L, 1);
968  lua_call(L, 2, 1);
969  return 1;
970 }
971 
972 void LuaAPI::fire_event(lua_State* L, Nation& nation, Event& event, bool& is_multi, const std::string_view extra) {
973  // Save the original event & momentarily replace it on the big world
974  // since the event callback **MIGHT** modify the event itself so we store
975  // a copy for each event everytime it fires
976  auto orig_event = event;
977 
978  // Call the "do event" function
979  Eng3D::Log::debug("event", Eng3D::translate_format("Event %s using lua#%i", event.ref_name.c_str(), event.do_event_function));
980  int nargs = 1;
981  lua_rawgeti(L, LUA_REGISTRYINDEX, event.do_event_function);
982  lua_pushstring(L, nation.ref_name.c_str());
983  if(!extra.empty()) {
984  lua_pushstring(L, extra.data());
985  nargs++;
986  }
987  if(g_world.lua.call_func(nargs, 1)) {
988  Eng3D::Log::error("lua", translate_format("lua_pcall failed: %s", lua_tostring(L, -1)));
989  lua_pop(L, 1);
990  goto restore_original;
991  }
992  is_multi = lua_toboolean(L, -1);
993  lua_pop(L, 1);
994 
995  {
996  // The changes done to the event "locally" are then created into a new local event
997  auto local_event = event;
998  local_event.cached_id = Event::invalid();
999  local_event.extra_data = std::string(extra);
1000  //local_event.ref_name = Eng3D::StringRef(string_format("%s:%s", local_event.ref_name.c_str(), nation.ref_name.c_str()).c_str());
1001  if(local_event.decisions.empty()) {
1002  Eng3D::Log::error("event", translate_format("Event %s has no decisions (ref_name=%s)", local_event.ref_name.c_str(), nation.ref_name.c_str()));
1003  } else {
1004  // Check that descisions have functions
1005  for(auto& descision : local_event.decisions) {
1006  descision.extra_data = local_event.extra_data;
1007  if(descision.do_decision_function == 0) {
1008  Eng3D::Log::error("event", translate_format("(Lua event %s on descision %s has no function callback", orig_event.ref_name.c_str(), descision.ref_name.c_str()));
1009  goto restore_original;
1010  }
1011  }
1012  nation.inbox.push_back(local_event);
1013  Eng3D::Log::debug("event", translate_format("Event triggered! %s (with %zu decisions)", local_event.ref_name.c_str(), local_event.decisions.size()));
1014  }
1015  }
1016 restore_original: // Original event then gets restored
1017  event = orig_event;
1018 }
1019 
1020 // Checks all events and their condition functions
1021 void LuaAPI::check_events(lua_State* L) {
1022  const std::scoped_lock lock(g_world.inbox_mutex);
1023  for(auto& event : g_world.events) {
1024  if(event.checked) continue;
1025  bool is_multi = true, has_fired = false;
1026  for(const auto nation_id : event.receiver_ids) {
1027  auto& nation = g_world.nations[nation_id];
1028  if(nation.exists()) {
1029  lua_rawgeti(L, LUA_REGISTRYINDEX, event.conditions_function);
1030  lua_pushstring(L, nation.ref_name.c_str());
1031  lua_pcall(L, 1, 1, 0);
1032  bool r = lua_toboolean(L, -1);
1033  lua_pop(L, 1);
1034  if(r) { // Conditions met
1035  has_fired = true;
1036  LuaAPI::fire_event(L, nation, event, is_multi, "");
1037  }
1038  }
1039  }
1040  // Event is marked as checked if it's not of multiple occurences
1041  if(has_fired && !is_multi)
1042  event.checked = true;
1043  }
1044 
1045  // Do decisions taken effects in the queue, then clear it awaiting other
1046  // taken decisions :)
1047  for(auto& [dec, nation_id] : g_world.taken_decisions) {
1048  const auto& nation = g_world.nations[nation_id];
1049  Eng3D::Log::debug("event", string_format("%s took the descision %i", nation.ref_name.c_str(), dec.do_decision_function));
1050  lua_rawgeti(L, LUA_REGISTRYINDEX, dec.do_decision_function);
1051  int nargs = 1;
1052  lua_pushstring(L, nation.ref_name.c_str());
1053  if(!dec.extra_data.get_string().empty()) {
1054  lua_pushstring(L, dec.extra_data.c_str());
1055  nargs++;
1056  }
1057  if(g_world.lua.call_func(nargs, 0)) {
1058  const std::string_view err_msg = lua_tostring(L, -1);
1059  lua_pop(L, 1);
1060  CXX_THROW(Eng3D::LuaException, string_format("%i(%s): %s", dec.do_decision_function, nation.ref_name.c_str(), err_msg).c_str());
1061  }
1062  }
1063  g_world.taken_decisions.clear();
1064 }
1065 
1066 #include "client/game_state.hpp"
1067 #include "action.hpp"
1068 #include "client/client_network.hpp"
1069 #include "server/server_network.hpp"
1070 #include "client/map.hpp"
1071 #include "client/map_render.hpp"
1072 int LuaAPI::ui_call_builtin(lua_State* L) {
1073  const std::string builtin_fn = luaL_checkstring(L, 1);
1074  auto& gs = static_cast<GameState&>(Eng3D::State::get_instance());
1075 
1076  if(builtin_fn == "gs.ai_control.form_packet") {
1077  //gs.client->send(Action::AiControl::form_packet(*gs.curr_nation));
1078  return 0;
1079  } else if(builtin_fn == "gs.ai_do_cmd_troops.get") {
1080  if(gs.curr_nation == nullptr)
1081  return 0;
1082  lua_pushboolean(L, gs.curr_nation->ai_do_cmd_troops);
1083  return 1;
1084  } else if(builtin_fn == "gs.ai_do_cmd_troops.set") {
1085  if(gs.curr_nation == nullptr)
1086  return 0;
1087  gs.curr_nation->ai_do_cmd_troops = lua_toboolean(L, 2);
1088  return 0;
1089  } else if(builtin_fn == "gs.map.reload_shaders") {
1090  gs.map->reload_shaders();
1091  return 0;
1092  } else if(builtin_fn == "gs.shader_opt.set") {
1093  auto options = gs.map->map_render->options.get_options();
1094  const std::string optname = luaL_checkstring(L, 2);
1095  for(const auto& option : options) {
1096  if(option.get_option() == optname) {
1097  bool is_used = lua_toboolean(L, 3);
1098  Eng3D::Log::debug("lua_bind", "Setting map_shader option " + optname + " to " + (is_used ? "TRUE" : "FALSE"));
1099  if(optname == "NOISE") {
1100  gs.map->map_render->options.noise.used = is_used;
1101  } else if(optname == "SDF") {
1102  gs.map->map_render->options.sdf.used = is_used;
1103  } else if(optname == "LIGHTING") {
1104  gs.map->map_render->options.lighting.used = is_used;
1105  } else if(optname == "CITY_LIGHTS") {
1106  gs.map->map_render->options.city_lights.used = is_used;
1107  } else if(optname == "PARALLAX") {
1108  gs.map->map_render->options.parallax.used = is_used;
1109  } else if(optname == "RIVERS") {
1110  gs.map->map_render->options.rivers.used = is_used;
1111  } else if(optname == "WATER") {
1112  gs.map->map_render->options.water.used = is_used;
1113  } else if(optname == "GRID") {
1114  gs.map->map_render->options.grid.used = is_used;
1115  } else if(optname == "UNITS") {
1116  gs.map->map_render->options.units.used = is_used;
1117  } else if(optname == "BUILDINGS") {
1118  gs.map->map_render->options.buildings.used = is_used;
1119  } else if(optname == "TREES") {
1120  gs.map->map_render->options.trees.used = is_used;
1121  }
1122  gs.map->reload_shaders();
1123  break;
1124  }
1125  }
1126  return 0;
1127  } else if(builtin_fn == "gs.shader_opt.get") {
1128  auto options = gs.map->map_render->options.get_options();
1129  const std::string optname = luaL_checkstring(L, 2);
1130  for(const auto& option : options) {
1131  if(option.get_option() == optname) {
1132  bool is_used = false;
1133  if(optname == "NOISE") {
1134  is_used = gs.map->map_render->options.noise.used;
1135  } else if(optname == "SDF") {
1136  is_used = gs.map->map_render->options.sdf.used;
1137  } else if(optname == "LIGHTING") {
1138  is_used = gs.map->map_render->options.lighting.used;
1139  } else if(optname == "CITY_LIGHTS") {
1140  is_used = gs.map->map_render->options.city_lights.used;
1141  } else if(optname == "PARALLAX") {
1142  is_used = gs.map->map_render->options.parallax.used;
1143  } else if(optname == "RIVERS") {
1144  is_used = gs.map->map_render->options.rivers.used;
1145  } else if(optname == "WATER") {
1146  is_used = gs.map->map_render->options.water.used;
1147  } else if(optname == "GRID") {
1148  is_used = gs.map->map_render->options.grid.used;
1149  } else if(optname == "UNITS") {
1150  is_used = gs.map->map_render->options.units.used;
1151  } else if(optname == "BUILDINGS") {
1152  is_used = gs.map->map_render->options.buildings.used;
1153  } else if(optname == "TREES") {
1154  is_used = gs.map->map_render->options.trees.used;
1155  }
1156  Eng3D::Log::debug("lua_bind", "Getting map_shader option " + optname + " is " + (is_used ? "TRUE" : "FALSE"));
1157  lua_pushboolean(L, is_used);
1158  return 1;
1159  }
1160  }
1161  return 0;
1162  } else if(builtin_fn == "gs.motion_blur.set") {
1163  gs.motion_blur = lua_toboolean(L, 2);
1164  return 0;
1165  } else if(builtin_fn == "gs.motion_blur.get") {
1166  lua_pushboolean(L, gs.motion_blur);
1167  return 1;
1168  } else if(builtin_fn == "gs.music_volume.set") {
1169  gs.audio_man.music_volume = luaL_checknumber(L, 2);
1170  return 0;
1171  } else if(builtin_fn == "gs.sound_volume.set") {
1172  gs.audio_man.sound_volume = luaL_checknumber(L, 2);
1173  return 0;
1174  } else if(builtin_fn == "gs.music_volume.get") {
1175  lua_pushnumber(L, gs.audio_man.music_volume);
1176  return 1;
1177  } else if(builtin_fn == "gs.sound_volume.get") {
1178  lua_pushnumber(L, gs.audio_man.sound_volume);
1179  return 1;
1180  }
1181 
1182  // Invalid callback name
1183  return 0;
1184 }
static State & get_instance()
Definition: state.cpp:514
std::deque< Event > inbox
Definition: nation.hpp:150
A single province, which is used to simulate economy in a "bulk-tiles" way instead of doing economica...
Definition: province.hpp:48
std::uint32_t color
Definition: province.hpp:97
Eng3D::StringRef name
Definition: province.hpp:96
std::vector< uint32_t > rgo_size
Definition: province.hpp:106
TerrainTypeId terrain_type_id
Definition: province.hpp:105
Roughly a batallion, consisting of approximately 500 soldiers each.
Definition: unit.hpp:80
void set_owner(const Nation &nation)
Definition: unit.cpp:184
void add_unit(Unit unit, ProvinceId unit_current_province)
Definition: unit.cpp:121
Eng3D::Freelist< Unit > units
Definition: unit.hpp:173
std::vector< UnitId > get_province_units(ProvinceId province_id) const
Definition: unit.hpp:165
UnitManager unit_manager
Definition: world.hpp:145
bool needs_to_sync
Used to signal the lua scripts of invalid operations (eg. adding a country midgame)
Definition: world.hpp:224
std::mutex inbox_mutex
Definition: world.hpp:227
std::vector< std::pair< Decision, NationId > > taken_decisions
Definition: world.hpp:228
Nation::Relation & get_relation(NationId a, NationId b)
Definition: world.hpp:192
T::Id get_id(const T &obj) const
Get the id of an object, this is a template for all types except for tiles and locally-stored types (...
Definition: world.hpp:179
void insert(T &ptr)
Definition: world.hpp:150
static World & get_instance()
Definition: world.hpp:121
Eng3D::LuaVM lua
Definition: world.hpp:216
TechnologyType
Definition: indpobj.hpp:297
void append_to_table(lua_State *L, int *index, float number)
Definition: lua_api.cpp:80
void error(const std::string_view category, const std::string_view msg)
Definition: log.cpp:68
void debug(const std::string_view category, const std::string_view msg)
Definition: log.cpp:58
std::string string_format(const std::string_view format, Args &&... args)
String formatter.
Definition: string.hpp:100
std::string translate_format(const std::string_view format, Args &&... args)
String formatter, with translation.
Definition: string.hpp:128
struct Rectangle Rect
Definition: rectangle.hpp:176
int province_add_unit(lua_State *L)
Definition: lua_api.cpp:544
int add_event(lua_State *L)
Definition: lua_api.cpp:682
int add_decision(lua_State *L)
Definition: lua_api.cpp:733
int get_religion_by_id(lua_State *L)
Definition: lua_api.cpp:915
int get_terrain_type(lua_State *L)
Definition: lua_api.cpp:141
int set_nation_relation(lua_State *L)
Definition: lua_api.cpp:405
int ui_call_builtin(lua_State *L)
Definition: lua_api.cpp:1072
int get_pop_type_by_id(lua_State *L)
Definition: lua_api.cpp:825
int add_province_pop(lua_State *L)
Definition: lua_api.cpp:635
int get_province_nuclei(lua_State *L)
Definition: lua_api.cpp:623
int add_province_owner(lua_State *L)
Definition: lua_api.cpp:675
int get_religion(lua_State *L)
Definition: lua_api.cpp:907
int get_terrain_type_by_id(lua_State *L)
Definition: lua_api.cpp:130
int update_province(lua_State *L)
Definition: lua_api.cpp:487
int add_req_technology_to_industry_type(lua_State *L)
Definition: lua_api.cpp:252
int get_good(lua_State *L)
Definition: lua_api.cpp:222
int add_event_receivers(lua_State *L)
Definition: lua_api.cpp:725
int add_good(lua_State *L)
Definition: lua_api.cpp:210
int get_province_by_id(lua_State *L)
Definition: lua_api.cpp:524
int get_technology(lua_State *L)
Definition: lua_api.cpp:166
int add_technology(lua_State *L)
Definition: lua_api.cpp:151
int add_req_good_unit_type(lua_State *L)
Definition: lua_api.cpp:955
int add_accepted_religion(lua_State *L)
Definition: lua_api.cpp:378
int set_nation_primary_language(lua_State *L)
Definition: lua_api.cpp:362
int add_pop_type(lua_State *L)
Definition: lua_api.cpp:745
int get_province_owner(lua_State *L)
Definition: lua_api.cpp:597
int get_province_neighbours(lua_State *L)
Definition: lua_api.cpp:611
int set_province_religion(lua_State *L)
Definition: lua_api.cpp:669
int add_input_to_industry_type(lua_State *L)
Definition: lua_api.cpp:229
int add_terrain_type(lua_State *L)
Definition: lua_api.cpp:114
int get_nation_relation(lua_State *L)
Definition: lua_api.cpp:395
int nation_declare_war_no_cb(lua_State *L)
Definition: lua_api.cpp:318
int add_nation_client_hint(lua_State *L)
Definition: lua_api.cpp:384
int add_req_tech_to_tech(lua_State *L)
Definition: lua_api.cpp:176
int add_output_to_industry_type(lua_State *L)
Definition: lua_api.cpp:237
int add_province_nucleus(lua_State *L)
Definition: lua_api.cpp:654
int add_unit_type(lua_State *L)
Definition: lua_api.cpp:923
int get_all_nations(lua_State *L)
Definition: lua_api.cpp:297
int switch_nation_soul(lua_State *L)
Definition: lua_api.cpp:309
int get_nation(lua_State *L)
Definition: lua_api.cpp:283
int add_accepted_language(lua_State *L)
Definition: lua_api.cpp:372
int update_province_building(lua_State *L)
Definition: lua_api.cpp:559
int get_provinces_with_nucleus_by_nation(lua_State *L)
Definition: lua_api.cpp:340
int register_new_table(lua_State *L, const std::string &name, const std::vector< luaL_Reg > meta, const std::vector< luaL_Reg > methods)
Definition: lua_api.cpp:43
int get_event(lua_State *L)
Definition: lua_api.cpp:713
int get_province_controller(lua_State *L)
Definition: lua_api.cpp:604
int get_provinces_owned_by_nation(lua_State *L)
Definition: lua_api.cpp:327
int set_province_language(lua_State *L)
Definition: lua_api.cpp:663
int nation_declare_unjustified_war(lua_State *L)
Definition: lua_api.cpp:415
int add_building_type(lua_State *L)
Definition: lua_api.cpp:182
void fire_event(lua_State *L, Nation &nation, Event &event, bool &is_multi, const std::string_view extra)
Definition: lua_api.cpp:972
int get_pop_type(lua_State *L)
Definition: lua_api.cpp:793
int get_province(lua_State *L)
Definition: lua_api.cpp:504
int get_language(lua_State *L)
Definition: lua_api.cpp:871
int add_language(lua_State *L)
Definition: lua_api.cpp:855
int give_hard_province_to(lua_State *L)
Definition: lua_api.cpp:575
int get_building_type(lua_State *L)
Definition: lua_api.cpp:197
int get_language_by_id(lua_State *L)
Definition: lua_api.cpp:883
int add_nation(lua_State *L)
Definition: lua_api.cpp:259
int add_province(lua_State *L)
Definition: lua_api.cpp:423
int add_req_good_to_industry_type(lua_State *L)
Definition: lua_api.cpp:245
int get_unit_type(lua_State *L)
Definition: lua_api.cpp:941
int rename_province(lua_State *L)
Definition: lua_api.cpp:648
int give_province_to(lua_State *L)
Definition: lua_api.cpp:569
int update_event(lua_State *L)
Definition: lua_api.cpp:700
int get_nation_by_id(lua_State *L)
Definition: lua_api.cpp:290
int set_nation_capital(lua_State *L)
Definition: lua_api.cpp:366
void check_events(lua_State *L)
Definition: lua_api.cpp:1021
int add_religion(lua_State *L)
Definition: lua_api.cpp:894
#define L
Definition: stb_vorbis.c:5131
Type for military outposts.
Definition: building.hpp:38
A commodity, mostly serves as a "product type".
Definition: product.hpp:35
Eng3D::StringRef effects
Definition: event.hpp:33
Eng3D::StringRef name
Definition: event.hpp:32
int do_decision_function
Definition: event.hpp:36
void for_each(const F &lambda) const
Definition: freelist.hpp:75
static int call_func(lua_State *L, int nargs, int nret)
Definition: luavm.cpp:344
const char * c_str() const
Definition: string.hpp:55
Id cached_id
Id used to speed up Id lookups on any context.
Definition: entity.hpp:106
constexpr static Id invalid()
Returns an invalid id.
Definition: entity.hpp:110
Definition: event.hpp:54
int do_event_function
Definition: event.hpp:65
Hints for the client on how to display the nation.
Definition: nation.hpp:82
IdeologyId ideology_id
Definition: nation.hpp:85
float alliance
Definition: nation.hpp:77
std::vector< float > luxury_needs_satisfaction
Definition: indpobj.hpp:244
std::vector< float > luxury_needs_deminishing_factor
Definition: indpobj.hpp:245
std::vector< float > basic_needs_amount
Definition: indpobj.hpp:243
float social_value
Definition: indpobj.hpp:242
Eng3D::StringRef name
Definition: indpobj.hpp:241
A product (based off a Commodity) which can be bought by POPs, converted by factories and transported...
Definition: product.hpp:54
Eng3D::StringRef ref_name
Definition: entity.hpp:161
std::vector< TechnologyId > req_technologies
Definition: indpobj.hpp:310
Defines a type of unit, it can be a tank, garrison, infantry, etc this is moddable via a lua script a...
Definition: unit.hpp:44
#define CXX_THROW(class,...)
Definition: utils.hpp:98
World g_world
Definition: world.cpp:59