sector_parser.cpp 12 KB

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