Symphony Of Empires
lua_save_util.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 // client/lua_save_util.cpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #include "lua_save_util.hpp"
26 
27 #include <filesystem>
28 
29 #include "eng3d/serializer.hpp"
30 #include "eng3d/utils.hpp"
31 #include "client/game_state.hpp"
32 #include "world.hpp"
33 #include "objects.hpp"
34 
35 static void save_province(GameState& gs, FILE* fp, Province& province)
36 {
37  if(province.neighbour_ids.empty()) return;
38  if(province.box_area.right == 0 &&
39  province.box_area.bottom == 0 &&
40  province.box_area.left == gs.world->width &&
41  province.box_area.top == gs.world->height) return;
42 
43  // Give the province a terrain
44  if(gs.world->terrain_types[province.terrain_type_id].is_water_body) {
45  for(auto& terrain : gs.world->terrain_types) {
46  if(terrain.is_water_body) continue;
47  province.terrain_type_id = terrain;
48  break;
49  }
50  }
51 
52  // Sea provinces dont have pops or RGOs
53  if(gs.world->terrain_types[province.terrain_type_id].is_water_body)
54  province.rgo_size.clear();
55 
56  // RGO
57  const uint32_t color = std::byteswap<std::uint32_t>((province.color & 0x00ffffff) << 8);
58  std::string rgo_size_out = "";
59  for(size_t i = 0; i < province.rgo_size.size(); i++) {
60  const auto& commodity = gs.world->commodities[i];
61  auto size = province.rgo_size[i];
62  if(size) {
63  rgo_size_out += string_format("{\"%s\",%zu},", commodity.ref_name.c_str(), size);
64  }
65  }
66 
67  const char *terrain_type_ref_name = gs.world->terrain_types[province.terrain_type_id].ref_name.c_str();
68  if (province.owner_id == ProvinceId(0))
69  terrain_type_ref_name = "sea";
70 
71  fprintf(fp, "province=Province:new{ref_name=\"%s\",name=translate(\"%s\"),color=0x%x,terrain=tt_%s,rgo_size={%s}}\n",
72  province.ref_name.c_str(),
73  province.name.c_str(),
74  (unsigned int)color,
75  terrain_type_ref_name,
76  rgo_size_out.c_str());
77  fprintf(fp, "province:register()\n");
78 
79  if(gs.world->terrain_types[province.terrain_type_id].is_water_body
80  || province.owner_id == ProvinceId(0))
81  return;
82 
83  for(auto& building : province.buildings)
84  building.level = 0;
85  province.buildings[rand() % province.buildings.size()].level = 10;
86  province.buildings[rand() % province.buildings.size()].level = 10;
87  for(const auto& building_type : gs.world->building_types) {
88  const auto& building = province.buildings[building_type];
89  if(building.level)
90  fprintf(fp, "province:create_building(bt_%s,%i)\n", building_type.ref_name.c_str(), (int)building.level);
91  }
92 
93  // POPs
94  for(const auto& pop : province.pops)
95  fprintf(fp, "province:add_pop(pt_%s,%f,%f)\n", gs.world->pop_types[pop.type_id].ref_name.c_str(), pop.size, pop.literacy);
96  for(const auto& language : gs.world->languages)
97  if(province.languages[language] > 0.f)
98  fprintf(fp, "province:set_language(c_%s,%f)\n", language.ref_name.c_str(), province.languages[language]);
99  for(const auto& religion : gs.world->religions)
100  if(province.religions[religion] > 0.f)
101  fprintf(fp, "province:set_religion(r_%s,%f)\n", religion.ref_name.c_str(), province.religions[religion]);
102  // Nuclei of the provinces
103  for(const auto& nucleus_id : province.nuclei)
104  fprintf(fp, "province:add_nucleus(n_%s)\n", gs.world->nations[nucleus_id].ref_name.c_str());
105  // Give province to owner
106  fprintf(fp, "province:give_to(n_%s)\n", gs.world->nations[province.owner_id].ref_name.c_str());
107  if(gs.world->nations[province.owner_id].capital_id == gs.world->get_id(province))
108  fprintf(fp, "n_%s:set_capital(province)\n", gs.world->nations[province.owner_id].ref_name.c_str());
109  // Units
110  for(const auto unit_id : gs.world->unit_manager.get_province_units(province)) {
111  auto& unit = gs.world->unit_manager.units[unit_id];
112  fprintf(fp, "province:add_unit(ut_%s,%zu)\n", gs.world->unit_types[unit.type_id].ref_name.c_str(), (size_t)unit.size);
113  }
114 }
115 
116 void LUA_util::save(GameState& gs, const std::string& savefile_path) {
117  if(gs.editor) {
118  std::filesystem::create_directory("editor");
119  std::filesystem::create_directory("editor/lua");
120  std::filesystem::create_directory("editor/lua/entities");
121  std::filesystem::create_directory("editor/map");
122  // Provinces
123  auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(fopen("editor/lua/entities/provinces.lua", "wt"), fclose);
124  size_t cnt = 0;
125  for(const auto& building_type : gs.world->building_types)
126  fprintf(fp.get(), "bt_%s=BuildingType:get(\"%s\")\n", building_type.ref_name.c_str(), building_type.ref_name.c_str());
127  for(const auto& unit_type : gs.world->unit_types)
128  fprintf(fp.get(), "ut_%s=UnitType:get(\"%s\")\n", unit_type.ref_name.c_str(), unit_type.ref_name.c_str());
129  for(const auto& terrain_type : gs.world->terrain_types)
130  fprintf(fp.get(), "tt_%s=TerrainType:get(\"%s\")\n", terrain_type.ref_name.c_str(), terrain_type.ref_name.c_str());
131  for(const auto& pop_type : gs.world->pop_types)
132  fprintf(fp.get(), "pt_%s=PopType:get(\"%s\")\n", pop_type.ref_name.c_str(), pop_type.ref_name.c_str());
133  for(const auto& language : gs.world->languages)
134  fprintf(fp.get(), "c_%s=Language:get(\"%s\")\n", language.ref_name.c_str(), language.ref_name.c_str());
135  for(const auto& religion : gs.world->religions)
136  fprintf(fp.get(), "r_%s=Religion:get(\"%s\")\n", religion.ref_name.c_str(), religion.ref_name.c_str());
137  for(const auto& nation : gs.world->nations)
138  fprintf(fp.get(), "n_%s=Nation:get(\"%s\")\n", nation.ref_name.c_str(), nation.ref_name.c_str());
139 
140  // First add provinces with pops, then the provinces **without** pops
141  for(auto& province : gs.world->provinces) {
142  if(province.is_populated())
143  save_province(gs, fp.get(), province);
144  cnt++;
145  }
146  for(auto& province : gs.world->provinces) {
147  if(!province.is_populated())
148  save_province(gs, fp.get(), province);
149  cnt++;
150  }
151  fp.reset();
152 
153  // Terrain types
154  fp = std::unique_ptr<FILE, int (*)(FILE*)>(fopen("editor/lua/entities/terrain_types.lua", "wt"), fclose);
155  cnt = 0;
156  for(const auto& terrain_type : gs.world->terrain_types) {
157  const uint32_t color = std::byteswap<std::uint32_t>((terrain_type.color & 0x00ffffff) << 8);
158  fprintf(fp.get(), "TerrainType:new{ref_name=\"%s\",name=translate(\"%s\"),color=0x%x,is_water_body=%s}:register()\n", terrain_type.ref_name.c_str(), terrain_type.name.c_str(), (unsigned int)color, terrain_type.is_water_body ? "true" : "false");
159  cnt++;
160  }
161  fp.reset();
162 
163  // Religions
164  fp = std::unique_ptr<FILE, int (*)(FILE*)>(fopen("editor/lua/entities/religions.lua", "wt"), fclose);
165  cnt = 0;
166  for(const auto& religion : gs.world->religions) {
167  const uint32_t color = std::byteswap<std::uint32_t>((religion.color & 0x00ffffff) << 8);
168  fprintf(fp.get(), "Religion:new{ref_name=\"%s\",name=translate(\"%s\"),color=0x%x}:register()\n", religion.ref_name.c_str(), religion.name.c_str(), (unsigned int)color);
169  cnt++;
170  }
171  fp.reset();
172 
173  // Pop types
174  fp = std::unique_ptr<FILE, int (*)(FILE*)>(fopen("editor/lua/entities/pop_types.lua", "wt"), fclose);
175  cnt = 0;
176  for(const auto& pop_type : gs.world->pop_types) {
177  fprintf(fp.get(), "PopType:new{ ref_name=\"%s\",name=translate(\"%s\"),social_value=%f}\n", pop_type.ref_name.c_str(), pop_type.name.c_str(), pop_type.social_value);
178  cnt++;
179  }
180  fp.reset();
181 
182  // Unit types
183  fp = std::unique_ptr<FILE, int (*)(FILE*)>(fopen("editor/lua/entities/unit_types.lua", "wt"), fclose);
184  cnt = 0;
185  fprintf(fp.get(), "local v = {}\n");
186  for(const auto& unit_type : gs.world->unit_types) {
187  if(unit_type.is_ground == true && unit_type.is_naval == false) {
188  fprintf(fp.get(), "v=UnitType:new{ref_name=\"%s\",name=translate(\"%s\"),defense=%f,attack=%f,health=%f,speed=%f}\n", unit_type.ref_name.c_str(), unit_type.name.c_str(), unit_type.defense, unit_type.attack, unit_type.max_health, unit_type.speed);
189  } else if(unit_type.is_ground == false && unit_type.is_naval == true) {
190  fprintf(fp.get(), "v=BoatType:new{ref_name=\"%s\",name=translate(\"%s\"),defense=%f,attack=%f,health=%f,speed=%f}\n", unit_type.ref_name.c_str(), unit_type.name.c_str(), unit_type.defense, unit_type.attack, unit_type.max_health, unit_type.speed);
191  } else if(unit_type.is_ground == true && unit_type.is_naval == true) {
192  fprintf(fp.get(), "v=AirplaneType:new{ref_name=\"%s\",name=translate(\"%s\"),defense=%f,attack=%f,health=%f,speed=%f}\n", unit_type.ref_name.c_str(), unit_type.name.c_str(), unit_type.defense, unit_type.attack, unit_type.max_health, unit_type.speed);
193  }
194  fprintf(fp.get(), "v:register()\n");
195  for(const auto& [good_id, amount] : unit_type.req_goods)
196  fprintf(fp.get(), "v:requires_good(Commodity:get(\"%s\"), %f)\n", gs.world->commodities[good_id].ref_name.c_str(), amount);
197  cnt++;
198  }
199  fp.reset();
200 
201  // Commodity types
202  fp = std::unique_ptr<FILE, int (*)(FILE*)>(fopen("editor/lua/entities/good_types.lua", "wt"), fclose);
203  fprintf(fp.get(), "-- Generated by editor :)\n");
204  for(const auto& good_type : gs.world->commodities)
205  fprintf(fp.get(), "Commodity:new{ref_name=\"%s\",name=translate(\"%s\")}:register()\n", good_type.ref_name.c_str(), good_type.name.c_str());
206  fp.reset();
207 
208  gs.ui_ctx.prompt("Save", "Editor data saved! (check editor folder)");
209  } else {
211  const auto nation_id = gs.curr_nation->get_id();
212  Eng3D::Deser::serialize(ar, nation_id);
214  ar.to_file(savefile_path);
215  gs.ui_ctx.prompt("Save", "Saved sucessfully!");
216  }
217 }
218 
219 void LUA_util::load(GameState& gs, const std::string& savefile_path) {
220  gs.paused = true;
221 
223  ar.from_file(savefile_path);
224  auto nation_id = gs.curr_nation->get_id();
225  Eng3D::Deser::deserialize(ar, nation_id);
227 
229  gs.world->events.clear();
230  gs.world->taken_decisions.clear();
231  for(auto& nation : gs.world->nations)
232  nation.inbox.clear();
233  gs.world->load_mod();
234  gs.ui_ctx.prompt("Loaded", "Loaded savefile");
235 }
UI::Context ui_ctx
Definition: state.hpp:128
std::atomic< bool > paused
Definition: game_state.hpp:151
Nation * curr_nation
Definition: game_state.hpp:158
World * world
Definition: game_state.hpp:156
A single province, which is used to simulate economy in a "bulk-tiles" way instead of doing economica...
Definition: province.hpp:48
std::vector< float > religions
Percentage of each religion prescence on the pops, from 0 to 1.
Definition: province.hpp:128
std::array< Pop, 7 > pops
Definition: province.hpp:107
Eng3D::Rect box_area
Definition: province.hpp:102
std::vector< ProvinceId > neighbour_ids
Definition: province.hpp:124
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
std::vector< NationId > nuclei
Definition: province.hpp:123
std::vector< float > languages
Percentage of each languages from 0 to 1.
Definition: province.hpp:126
bool is_populated() const
Definition: province.hpp:84
TerrainTypeId terrain_type_id
Definition: province.hpp:105
NationId owner_id
Definition: province.hpp:103
std::vector< Building > buildings
Definition: province.hpp:110
void prompt(const std::string &title, const std::string &text)
Definition: ui.cpp:183
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
void load_mod()
Definition: world.cpp:590
std::vector< std::pair< Decision, NationId > > taken_decisions
Definition: world.hpp:228
size_t width
Definition: world.hpp:220
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
size_t height
Definition: world.hpp:220
void deserialize(Eng3D::Deser::Archive &ar, T &obj)
Definition: serializer.hpp:154
void serialize(Eng3D::Deser::Archive &ar, const T &obj)
Definition: serializer.hpp:144
std::string string_format(const std::string_view format, Args &&... args)
String formatter.
Definition: string.hpp:100
void save(GameState &gs, const std::string &savefile_path)
void load(GameState &gs, const std::string &savefile_path)
Base class that serves as archiver, stores (in memory) the data required for serialization/deserializ...
Definition: serializer.hpp:64
void from_file(const ::std::string &path)
Definition: serializer.cpp:61
const char * c_str() const
Definition: string.hpp:55
constexpr Id get_id() const
Definition: entity.hpp:152
Eng3D::StringRef ref_name
Definition: entity.hpp:161