coin.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. // SuperTux
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  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 <http://www.gnu.org/licenses/>.
  16. #include "object/coin.hpp"
  17. #include "audio/sound_manager.hpp"
  18. #include "audio/sound_source.hpp"
  19. #include "editor/editor.hpp"
  20. #include "object/bouncy_coin.hpp"
  21. #include "object/player.hpp"
  22. #include "object/tilemap.hpp"
  23. #include "supertux/flip_level_transformer.hpp"
  24. #include "supertux/level.hpp"
  25. #include "supertux/sector.hpp"
  26. #include "util/reader_mapping.hpp"
  27. #include "util/writer.hpp"
  28. Coin::Coin(const Vector& pos, bool count_stats, const std::string& sprite_path) :
  29. MovingSprite(pos, sprite_path, LAYER_OBJECTS - 1, COLGROUP_TOUCHABLE),
  30. PathObject(),
  31. m_offset(0.0f, 0.0f),
  32. m_from_tilemap(false),
  33. m_add_path(false),
  34. m_physic(),
  35. m_collect_script(),
  36. m_starting_node(0),
  37. m_count_stats(count_stats)
  38. {
  39. SoundManager::current()->preload("sounds/coin.wav");
  40. }
  41. Coin::Coin(const ReaderMapping& reader, bool count_stats) :
  42. MovingSprite(reader, "images/objects/coin/coin.sprite", LAYER_OBJECTS - 1, COLGROUP_TOUCHABLE),
  43. PathObject(),
  44. m_offset(0.0f, 0.0f),
  45. m_from_tilemap(false),
  46. m_add_path(false),
  47. m_physic(),
  48. m_collect_script(),
  49. m_starting_node(0),
  50. m_count_stats(count_stats)
  51. {
  52. reader.get("starting-node", m_starting_node, 0);
  53. reader.get("collect-script", m_collect_script, "");
  54. parse_type(reader);
  55. init_path(reader, true);
  56. SoundManager::current()->preload("sounds/coin.wav");
  57. }
  58. GameObjectTypes
  59. Coin::get_types() const
  60. {
  61. return {
  62. { "normal", _("Normal") },
  63. { "retro", _("Retro") }
  64. };
  65. }
  66. std::string
  67. Coin::get_default_sprite_name() const
  68. {
  69. switch (m_type)
  70. {
  71. case RETRO:
  72. return "images/objects/coin/retro_coin.sprite";
  73. default:
  74. return m_default_sprite_name;
  75. }
  76. }
  77. void
  78. Coin::finish_construction()
  79. {
  80. if (get_path())
  81. {
  82. if (m_starting_node >= static_cast<int>(get_path()->get_nodes().size()))
  83. m_starting_node = static_cast<int>(get_path()->get_nodes().size()) - 1;
  84. set_pos(m_path_handle.get_pos(m_col.m_bbox.get_size(), get_path()->get_nodes()[m_starting_node].position));
  85. get_walker()->jump_to_node(m_starting_node);
  86. }
  87. m_add_path = get_walker() && get_path() && get_path()->is_valid();
  88. }
  89. void
  90. Coin::update(float dt_sec)
  91. {
  92. // If we have a path to follow, follow it.
  93. if (get_walker()) {
  94. Vector v(0.0f, 0.0f);
  95. if (m_from_tilemap)
  96. {
  97. v = m_offset + get_walker()->get_pos(m_col.m_bbox.get_size(), m_path_handle);
  98. }
  99. else
  100. {
  101. get_walker()->update(dt_sec);
  102. v = get_walker()->get_pos(m_col.m_bbox.get_size(), m_path_handle);
  103. }
  104. if (get_path() && get_path()->is_valid()) {
  105. m_col.set_movement(v - get_pos());
  106. }
  107. }
  108. }
  109. void
  110. Coin::editor_update()
  111. {
  112. if (get_walker()) {
  113. if (m_from_tilemap) {
  114. set_pos(m_offset + get_walker()->get_pos(m_col.m_bbox.get_size(), m_path_handle));
  115. } else {
  116. set_pos(get_walker()->get_pos(m_col.m_bbox.get_size(), m_path_handle));
  117. if (!get_path()) return;
  118. if (!get_path()->is_valid()) return;
  119. if (m_starting_node >= static_cast<int>(get_path()->get_nodes().size()))
  120. m_starting_node = static_cast<int>(get_path()->get_nodes().size()) - 1;
  121. set_pos(m_path_handle.get_pos(m_col.m_bbox.get_size(), get_path()->get_nodes()[m_starting_node].position));
  122. }
  123. }
  124. }
  125. void
  126. Coin::save_state()
  127. {
  128. MovingSprite::save_state();
  129. PathObject::save_state();
  130. }
  131. void
  132. Coin::check_state()
  133. {
  134. MovingSprite::check_state();
  135. PathObject::check_state();
  136. }
  137. void
  138. Coin::collect()
  139. {
  140. static Timer sound_timer;
  141. static int pitch_one = 128;
  142. static float last_pitch = 1;
  143. float pitch = 1;
  144. int tile = static_cast<int>(get_pos().y / 32);
  145. if (!sound_timer.started()) {
  146. pitch_one = tile;
  147. pitch = 1;
  148. last_pitch = 1;
  149. } else if (sound_timer.get_timegone() < 0.02f) {
  150. pitch = last_pitch;
  151. } else {
  152. switch ((pitch_one - tile) % 7) {
  153. case -6:
  154. pitch = 1.f/2; // C.
  155. break;
  156. case -5:
  157. pitch = 5.f/8; // E.
  158. break;
  159. case -4:
  160. pitch = 4.f/6; // F.
  161. break;
  162. case -3:
  163. pitch = 3.f/4; // G.
  164. break;
  165. case -2:
  166. pitch = 5.f/6; // A.
  167. break;
  168. case -1:
  169. pitch = 9.f/10; // Bb.
  170. break;
  171. case 0:
  172. pitch = 1.f; // c.
  173. break;
  174. case 1:
  175. pitch = 9.f/8; // d.
  176. break;
  177. case 2:
  178. pitch = 5.f/4; // e.
  179. break;
  180. case 3:
  181. pitch = 4.f/3; // f.
  182. break;
  183. case 4:
  184. pitch = 3.f/2; // g.
  185. break;
  186. case 5:
  187. pitch = 5.f/3; // a.
  188. break;
  189. case 6:
  190. pitch = 9.f/5; // bb.
  191. break;
  192. }
  193. last_pitch = pitch;
  194. }
  195. sound_timer.start(1);
  196. std::unique_ptr<SoundSource> soundSource = SoundManager::current()->create_sound_source("sounds/coin.wav");
  197. soundSource->set_position(get_pos());
  198. soundSource->set_pitch(pitch);
  199. soundSource->play();
  200. SoundManager::current()->manage_source(std::move(soundSource));
  201. Sector::get().get_players()[0]->get_status().add_coins(1, false);
  202. Sector::get().add<BouncyCoin>(get_pos(), false, get_sprite_name());
  203. if (m_count_stats && !m_parent_dispenser)
  204. Sector::get().get_level().m_stats.increment_coins();
  205. remove_me();
  206. if (!m_collect_script.empty()) {
  207. Sector::get().run_script(m_collect_script, "collect-script");
  208. }
  209. }
  210. HitResponse
  211. Coin::collision(GameObject& other, const CollisionHit& )
  212. {
  213. auto player = dynamic_cast<Player*>(&other);
  214. if (player == nullptr)
  215. return ABORT_MOVE;
  216. if (m_col.get_bbox().overlaps(player->get_bbox().grown(-0.1f)))
  217. collect();
  218. return ABORT_MOVE;
  219. }
  220. /* The following defines a coin subject to gravity. */
  221. HeavyCoin::HeavyCoin(const Vector& pos, const Vector& init_velocity, bool count_stats, const std::string& sprite_path) :
  222. Coin(pos, count_stats, sprite_path),
  223. m_physic(),
  224. m_last_hit()
  225. {
  226. m_physic.enable_gravity(true);
  227. SoundManager::current()->preload("sounds/coin2.ogg");
  228. set_group(COLGROUP_MOVING);
  229. m_physic.set_velocity(init_velocity);
  230. }
  231. HeavyCoin::HeavyCoin(const ReaderMapping& reader, bool count_stats) :
  232. Coin(reader, count_stats),
  233. m_physic(),
  234. m_last_hit()
  235. {
  236. m_physic.enable_gravity(true);
  237. SoundManager::current()->preload("sounds/coin2.ogg");
  238. set_group(COLGROUP_MOVING);
  239. }
  240. void
  241. HeavyCoin::update(float dt_sec)
  242. {
  243. // Enable physics.
  244. m_col.set_movement(m_physic.get_movement(dt_sec));
  245. }
  246. void
  247. HeavyCoin::collision_solid(const CollisionHit& hit)
  248. {
  249. float clink_threshold = 100.0f; // Sets the minimum speed needed to result in collision noise.
  250. // TODO: Colliding with HeavyCoins should have their own unique sound.
  251. if (hit.bottom) {
  252. if (m_physic.get_velocity_y() > clink_threshold && !m_last_hit.bottom)
  253. SoundManager::current()->play("sounds/coin2.ogg", get_pos());
  254. if (m_physic.get_velocity_y() > 200) {// lets some coins bounce
  255. m_physic.set_velocity_y(-99);
  256. } else {
  257. m_physic.set_velocity(0, 0);
  258. }
  259. }
  260. if (hit.right || hit.left) {
  261. if ((m_physic.get_velocity_x() > clink_threshold ||
  262. m_physic.get_velocity_x()< -clink_threshold) &&
  263. hit.right != m_last_hit.right && hit.left != m_last_hit.left)
  264. SoundManager::current()->play("sounds/coin2.ogg", get_pos());
  265. m_physic.set_velocity_x(-m_physic.get_velocity_x());
  266. }
  267. if (hit.top) {
  268. if (m_physic.get_velocity_y() < -clink_threshold && !m_last_hit.top)
  269. SoundManager::current()->play("sounds/coin2.ogg", get_pos());
  270. m_physic.set_velocity_y(-m_physic.get_velocity_y());
  271. }
  272. // Only make a sound if the coin wasn't hittin anything last frame (A coin
  273. // stuck in solid matter would flood the sound manager - see #1555 on GitHub).
  274. m_last_hit = hit;
  275. }
  276. void
  277. Coin::move_to(const Vector& pos)
  278. {
  279. Vector shift = pos - m_col.m_bbox.p1();
  280. if (get_path()) {
  281. get_path()->move_by(shift);
  282. }
  283. set_pos(pos);
  284. }
  285. ObjectSettings
  286. Coin::get_settings()
  287. {
  288. ObjectSettings result = MovingSprite::get_settings();
  289. result.add_path_ref(_("Path"), *this, get_path_ref(), "path-ref");
  290. m_add_path = get_walker() && get_path() && get_path()->is_valid();
  291. result.add_bool(_("Following path"), &m_add_path);
  292. if (get_walker() && get_path() && get_path()->is_valid()) {
  293. result.add_walk_mode(_("Path Mode"), &get_path()->m_mode, {}, {});
  294. result.add_bool(_("Adapt Speed"), &get_path()->m_adapt_speed, {}, {});
  295. result.add_int(_("Starting Node"), &m_starting_node, "starting-node", 0, 0U);
  296. result.add_path_handle(_("Handle"), m_path_handle, "handle");
  297. }
  298. result.add_script(_("Collect script"), &m_collect_script, "collect-script");
  299. result.reorder({"collect-script", "path-ref"});
  300. return result;
  301. }
  302. void
  303. Coin::after_editor_set()
  304. {
  305. MovingSprite::after_editor_set();
  306. if (get_walker() && get_path()->is_valid()) {
  307. if (!m_add_path) {
  308. get_path()->m_nodes.clear();
  309. }
  310. } else {
  311. if (m_add_path) {
  312. init_path_pos(m_col.m_bbox.p1());
  313. }
  314. }
  315. }
  316. void
  317. Coin::on_flip(float height)
  318. {
  319. MovingSprite::on_flip(height);
  320. PathObject::on_flip();
  321. FlipLevelTransformer::transform_flip(m_flip);
  322. }
  323. ObjectSettings
  324. HeavyCoin::get_settings()
  325. {
  326. auto result = MovingSprite::get_settings();
  327. result.add_script(_("Collect script"), &m_collect_script, "collect-script");
  328. result.reorder({"collect-script", "sprite", "x", "y"});
  329. return result;
  330. }
  331. void
  332. HeavyCoin::after_editor_set()
  333. {
  334. MovingSprite::after_editor_set();
  335. }
  336. void
  337. HeavyCoin::on_flip(float height)
  338. {
  339. // Call on_flip from grandparent class MovingSprite to
  340. // avoid flipping of gravity-affected object HeavyCoin.
  341. MovingSprite::on_flip(height);
  342. }
  343. /* EOF */