sector_parser.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // SuperTux
  2. // Copyright (C) 2015 Ingo Ruhnke <grumbel@gmail.com>
  3. // 2023 Vankata453
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #include "supertux/sector_parser.hpp"
  18. #include <iostream>
  19. #include <physfs.h>
  20. #include <sexp/value.hpp>
  21. #include "badguy/fish_jumping.hpp"
  22. #include "badguy/jumpy.hpp"
  23. #include "editor/editor.hpp"
  24. #include "object/ambient_light.hpp"
  25. #include "object/background.hpp"
  26. #include "object/camera.hpp"
  27. #include "object/cloud_particle_system.hpp"
  28. #include "object/custom_particle_system.hpp"
  29. #include "object/gradient.hpp"
  30. #include "object/music_object.hpp"
  31. #include "object/rain_particle_system.hpp"
  32. #include "object/snow_particle_system.hpp"
  33. #include "object/spawnpoint.hpp"
  34. #include "object/tilemap.hpp"
  35. #include "supertux/game_object_factory.hpp"
  36. #include "supertux/level.hpp"
  37. #include "supertux/sector.hpp"
  38. #include "supertux/tile.hpp"
  39. #include "util/reader_collection.hpp"
  40. #include "util/reader_mapping.hpp"
  41. #include "worldmap/spawn_point.hpp"
  42. static const std::string DEFAULT_BG = "images/background/antarctic/arctis2.png";
  43. std::unique_ptr<Sector>
  44. SectorParser::from_reader(Level& level, const ReaderMapping& reader, bool editable)
  45. {
  46. auto sector = std::make_unique<Sector>(level);
  47. BIND_SECTOR(*sector);
  48. SectorParser parser(*sector, editable);
  49. parser.parse(reader);
  50. return sector;
  51. }
  52. std::unique_ptr<Sector>
  53. SectorParser::from_reader_old_format(Level& level, const ReaderMapping& reader, bool editable)
  54. {
  55. auto sector = std::make_unique<Sector>(level);
  56. BIND_SECTOR(*sector);
  57. SectorParser parser(*sector, editable);
  58. parser.parse_old_format(reader);
  59. return sector;
  60. }
  61. std::unique_ptr<Sector>
  62. SectorParser::from_nothing(Level& level)
  63. {
  64. auto sector = std::make_unique<Sector>(level);
  65. BIND_SECTOR(*sector);
  66. SectorParser parser(*sector, false);
  67. parser.create_sector();
  68. return sector;
  69. }
  70. SectorParser::SectorParser(Base::Sector& sector, bool editable) :
  71. m_sector(sector),
  72. m_editable(editable)
  73. {
  74. }
  75. std::unique_ptr<GameObject>
  76. SectorParser::parse_object(const std::string& name, const ReaderMapping& reader)
  77. {
  78. if (parse_object_additional(name, reader))
  79. return {}; // Object was parsed by additional rules, so cancel regular object parsing.
  80. try
  81. {
  82. return GameObjectFactory::instance().create(name, reader);
  83. }
  84. catch (std::exception& err)
  85. {
  86. log_warning << err.what() << std::endl;
  87. return {};
  88. }
  89. }
  90. bool
  91. SectorParser::parse_object_additional(const std::string& name, const ReaderMapping& reader)
  92. {
  93. return false; // No additional object parsing rules, continue with regular object parsing.
  94. }
  95. void
  96. SectorParser::parse(const ReaderMapping& reader)
  97. {
  98. auto iter = reader.get_iter();
  99. while (iter.next()) {
  100. if (iter.get_key() == "name")
  101. {
  102. std::string value;
  103. iter.get(value);
  104. m_sector.set_name(value);
  105. }
  106. else if (iter.get_key() == "gravity")
  107. {
  108. auto sector = dynamic_cast<Sector*>(&m_sector);
  109. if (!sector) continue;
  110. float value;
  111. iter.get(value);
  112. sector->set_gravity(value);
  113. }
  114. else if (iter.get_key() == "music")
  115. {
  116. const auto& sx = iter.get_sexp();
  117. if (sx.is_array() && sx.as_array().size() == 2 && sx.as_array()[1].is_string()) {
  118. std::string value;
  119. iter.get(value);
  120. m_sector.add<MusicObject>().set_music(value);
  121. } else {
  122. m_sector.add<MusicObject>(iter.as_mapping());
  123. }
  124. }
  125. else if (iter.get_key() == "init-script")
  126. {
  127. std::string value;
  128. iter.get(value);
  129. m_sector.set_init_script(value);
  130. }
  131. else if (iter.get_key() == "ambient-light")
  132. {
  133. const auto& sx = iter.get_sexp();
  134. if (sx.is_array() && sx.as_array().size() >= 3 &&
  135. sx.as_array()[1].is_real() && sx.as_array()[2].is_real() && sx.as_array()[3].is_real())
  136. {
  137. // for backward compatibilty
  138. std::vector<float> vColor;
  139. bool hasColor = reader.get("ambient-light", vColor);
  140. if (vColor.size() < 3 || !hasColor) {
  141. log_warning << "(ambient-light) requires a color as argument" << std::endl;
  142. } else {
  143. m_sector.add<AmbientLight>(Color(vColor));
  144. }
  145. } else {
  146. // modern format
  147. m_sector.add<AmbientLight>(iter.as_mapping());
  148. }
  149. }
  150. else
  151. {
  152. auto object = parse_object(iter.get_key(), iter.as_mapping());
  153. if (object)
  154. m_sector.add_object(std::move(object));
  155. }
  156. }
  157. m_sector.finish_construction(m_editable);
  158. }
  159. void
  160. SectorParser::parse_old_format(const ReaderMapping& reader)
  161. {
  162. m_sector.set_name("main");
  163. auto sector = dynamic_cast<Sector*>(&m_sector);
  164. if (sector)
  165. {
  166. float gravity;
  167. if (reader.get("gravity", gravity))
  168. sector->set_gravity(gravity);
  169. }
  170. std::string backgroundimage;
  171. if (reader.get("background", backgroundimage) && (!backgroundimage.empty())) {
  172. // These paths may need to be changed.
  173. if (backgroundimage == "arctis.png") backgroundimage = "arctis.jpg";
  174. if (backgroundimage == "arctis2.jpg") backgroundimage = "arctis.jpg";
  175. if (backgroundimage == "ocean.png") backgroundimage = "ocean.jpg";
  176. backgroundimage = "images/background/" + backgroundimage;
  177. if (!PHYSFS_exists(backgroundimage.c_str())) {
  178. log_warning << "Background image \"" << backgroundimage << "\" not found. Ignoring." << std::endl;
  179. backgroundimage = "";
  180. }
  181. }
  182. float bgspeed = .5;
  183. reader.get("bkgd_speed", bgspeed);
  184. bgspeed /= 100;
  185. Color bkgd_top, bkgd_bottom;
  186. int r = 0, g = 0, b = 128;
  187. reader.get("bkgd_red_top", r);
  188. reader.get("bkgd_green_top", g);
  189. reader.get("bkgd_blue_top", b);
  190. bkgd_top.red = static_cast<float> (r) / 255.0f;
  191. bkgd_top.green = static_cast<float> (g) / 255.0f;
  192. bkgd_top.blue = static_cast<float> (b) / 255.0f;
  193. reader.get("bkgd_red_bottom", r);
  194. reader.get("bkgd_green_bottom", g);
  195. reader.get("bkgd_blue_bottom", b);
  196. bkgd_bottom.red = static_cast<float> (r) / 255.0f;
  197. bkgd_bottom.green = static_cast<float> (g) / 255.0f;
  198. bkgd_bottom.blue = static_cast<float> (b) / 255.0f;
  199. if (!backgroundimage.empty()) {
  200. auto& background = m_sector.add<Background>();
  201. background.set_image(backgroundimage);
  202. background.set_speed(bgspeed);
  203. } else {
  204. auto& gradient = m_sector.add<Gradient>();
  205. gradient.set_gradient(bkgd_top, bkgd_bottom);
  206. }
  207. std::string particlesystem;
  208. reader.get("particle_system", particlesystem);
  209. if (particlesystem == "clouds")
  210. m_sector.add<CloudParticleSystem>();
  211. else if (particlesystem == "snow")
  212. m_sector.add<SnowParticleSystem>();
  213. else if (particlesystem == "rain")
  214. m_sector.add<RainParticleSystem>();
  215. else if (particlesystem == "custom-particles")
  216. m_sector.add<CustomParticleSystem>();
  217. Vector startpos(100, 170);
  218. reader.get("start_pos_x", startpos.x);
  219. reader.get("start_pos_y", startpos.y);
  220. m_sector.add<SpawnPointMarker>("main", startpos);
  221. m_sector.add<MusicObject>().set_music("music/chipdisko.ogg");
  222. // skip reading music filename. It's all .ogg now, anyway
  223. /*
  224. reader.get("music", music);
  225. m_sector.set_music("music/" + m_sector.get_music());
  226. */
  227. int width = 30, height = 15;
  228. reader.get("width", width);
  229. reader.get("height", height);
  230. std::vector<unsigned int> tiles;
  231. if (reader.get("interactive-tm", tiles)
  232. || reader.get("tilemap", tiles)) {
  233. auto& tilemap = m_sector.add<TileMap>(m_sector.get_tileset());
  234. tilemap.set(width, height, tiles, LAYER_TILES, true);
  235. // replace tile id 112 (old invisible tile) with 1311 (new invisible tile)
  236. for (int x=0; x < tilemap.get_width(); ++x) {
  237. for (int y=0; y < tilemap.get_height(); ++y) {
  238. uint32_t id = tilemap.get_tile_id(x, y);
  239. if (id == 112)
  240. tilemap.change(x, y, 1311);
  241. }
  242. }
  243. if (height < 19) tilemap.resize(width, 19);
  244. }
  245. if (reader.get("background-tm", tiles)) {
  246. auto& tilemap = m_sector.add<TileMap>(m_sector.get_tileset());
  247. tilemap.set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
  248. if (height < 19) tilemap.resize(width, 19);
  249. }
  250. if (reader.get("foreground-tm", tiles)) {
  251. auto& tilemap = m_sector.add<TileMap>(m_sector.get_tileset());
  252. tilemap.set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
  253. // fill additional space in foreground with tiles of ID 2035 (lightmap/black)
  254. if (height < 19) tilemap.resize(width, 19, 2035);
  255. }
  256. // read reset-points (now spawn-points)
  257. std::optional<ReaderMapping> resetpoints;
  258. if (reader.get("reset-points", resetpoints)) {
  259. auto iter = resetpoints->get_iter();
  260. while (iter.next()) {
  261. if (iter.get_key() == "point") {
  262. Vector sp_pos(0.0f, 0.0f);
  263. if (reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y))
  264. {
  265. m_sector.add<SpawnPointMarker>("main", sp_pos);
  266. }
  267. } else {
  268. log_warning << "Unknown token '" << iter.get_key() << "' in reset-points." << std::endl;
  269. }
  270. }
  271. }
  272. // read objects
  273. std::optional<ReaderCollection> objects;
  274. if (reader.get("objects", objects)) {
  275. for (auto const& obj : objects->get_objects())
  276. {
  277. auto object = parse_object(obj.get_name(), obj.get_mapping());
  278. if (object) {
  279. m_sector.add_object(std::move(object));
  280. } else {
  281. log_warning << "Unknown object '" << obj.get_name() << "' in level." << std::endl;
  282. }
  283. }
  284. }
  285. // add a camera
  286. auto camera_ = std::make_unique<Camera>("Camera");
  287. m_sector.add_object(std::move(camera_));
  288. m_sector.flush_game_objects();
  289. if (m_sector.get_solid_tilemaps().empty()) {
  290. log_warning << "sector '" << m_sector.get_name() << "' does not contain a solid tile layer." << std::endl;
  291. }
  292. m_sector.finish_construction(m_editable);
  293. }
  294. void
  295. SectorParser::create_sector()
  296. {
  297. if (!m_sector.in_worldmap())
  298. {
  299. auto& background = m_sector.add<Background>();
  300. background.set_image(DEFAULT_BG);
  301. background.set_speed(0.5);
  302. auto& bkgrd = m_sector.add<TileMap>(m_sector.get_tileset());
  303. bkgrd.resize(100, 35);
  304. bkgrd.set_layer(-100);
  305. bkgrd.set_solid(false);
  306. auto& frgrd = m_sector.add<TileMap>(m_sector.get_tileset());
  307. frgrd.resize(100, 35);
  308. frgrd.set_layer(100);
  309. frgrd.set_solid(false);
  310. // Add background gradient to sector:
  311. auto& gradient = m_sector.add<Gradient>();
  312. gradient.set_gradient(Color(0.3f, 0.4f, 0.75f), Color::WHITE);
  313. gradient.set_layer(-301);
  314. }
  315. else
  316. {
  317. auto& water = m_sector.add<TileMap>(m_sector.get_tileset());
  318. water.resize(100, 35, 1);
  319. water.set_layer(-100);
  320. water.set_solid(false);
  321. }
  322. auto& intact = m_sector.add<TileMap>(m_sector.get_tileset());
  323. if (m_sector.in_worldmap()) {
  324. intact.resize(100, 100, 0);
  325. } else {
  326. intact.resize(100, 35, 0);
  327. }
  328. intact.set_layer(0);
  329. intact.set_solid(true);
  330. if (m_sector.in_worldmap()) {
  331. m_sector.add<worldmap::SpawnPointObject>("main", Vector(4, 4));
  332. } else {
  333. m_sector.add<SpawnPointMarker>("main", Vector(64, 480));
  334. }
  335. m_sector.add<Camera>("Camera");
  336. m_sector.add<MusicObject>();
  337. m_sector.finish_construction(m_editable);
  338. }
  339. /* EOF */