key.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // SuperTux
  2. // Copyright (C) 2022 Daniel Ward <weluvgoatz@gmail.com>
  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/key.hpp"
  17. #include "audio/sound_manager.hpp"
  18. #include "math/random.hpp"
  19. #include "math/util.hpp"
  20. #include "object/player.hpp"
  21. #include "object/sprite_particle.hpp"
  22. #include "sprite/sprite.hpp"
  23. #include "sprite/sprite_manager.hpp"
  24. #include "supertux/sector.hpp"
  25. #include "trigger/door.hpp"
  26. #include "util/reader_mapping.hpp"
  27. Key::Key(const ReaderMapping& reader) :
  28. MovingSprite(reader, "images/objects/keys/key.sprite", LAYER_OBJECTS, COLGROUP_TOUCHABLE),
  29. m_pos_list(),
  30. m_collected(),
  31. m_state(KeyState::NORMAL),
  32. m_wait_timer(),
  33. m_unlock_timer(),
  34. m_physic(),
  35. m_chain_pos(1),
  36. m_my_door_pos(0.f, 0.f),
  37. m_color(Color::WHITE),
  38. m_owner(),
  39. m_lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-small.sprite")),
  40. m_total_time_elapsed(),
  41. m_target_speed()
  42. {
  43. std::vector<float> vColor;
  44. if (reader.get("color", vColor)) {
  45. m_color = Color(vColor);
  46. }
  47. m_lightsprite->set_blend(Blend::ADD);
  48. m_lightsprite->set_color(m_color);
  49. // TODO: Add proper sound
  50. SoundManager::current()->preload("sounds/metal_hit.ogg");
  51. m_sprite->set_color(m_color);
  52. m_physic.enable_gravity(false);
  53. }
  54. void
  55. Key::update(float dt_sec)
  56. {
  57. m_target_speed = 12.f * std::cos(m_total_time_elapsed * 2.f);
  58. if (m_state == KeyState::NORMAL)
  59. {
  60. m_total_time_elapsed += dt_sec;
  61. m_col.set_movement(Vector(0.f, m_target_speed*dt_sec));
  62. }
  63. if (!m_owner || !m_owner->is_dead())
  64. {
  65. float px = graphicsRandom.randf(get_bbox().get_left(), get_bbox().get_right());
  66. float py = graphicsRandom.randf(get_bbox().get_top(), get_bbox().get_bottom());
  67. Vector ppos = Vector(px, py);
  68. int upper_limit = m_state == KeyState::NORMAL ? 20 : 50;
  69. bool spawn_particle_now = (graphicsRandom.rand(0, upper_limit) == 5);
  70. if (spawn_particle_now)
  71. {
  72. Sector::get().add<SpriteParticle>(
  73. "images/particles/sparkle.sprite", "small",
  74. ppos, ANCHOR_MIDDLE, Vector(0, 0), Vector(0, 0), LAYER_OBJECTS + 6, false, m_color);
  75. }
  76. }
  77. if (!m_owner)
  78. return;
  79. float distance = glm::length(get_pos() - m_owner->get_pos());
  80. if (m_state != KeyState::NORMAL)
  81. {
  82. m_pos_list.push_front((m_owner->get_bbox().get_middle())-
  83. Vector(get_bbox().get_width()/2.f, get_bbox().get_height()/2.f)-
  84. Vector(0.f, 10.f));
  85. }
  86. Vector m_goal_pos = m_pos_list.back();
  87. while (m_pos_list.size() > unsigned(20 * m_chain_pos))
  88. m_pos_list.pop_back();
  89. // use
  90. for (auto& door : Sector::get().get_objects_by_type<Door>()) {
  91. if (m_state == KeyState::FOLLOW && door.is_locked() && glm::length(get_pos() - door.get_pos()) < 100.f &&
  92. // color matches
  93. std::abs(door.get_lock_color().red - m_color.red) <= 0.1f &&
  94. std::abs(door.get_lock_color().green - m_color.green) <= 0.1f &&
  95. std::abs(door.get_lock_color().blue - m_color.blue) <= 0.1f) {
  96. m_owner->remove_collected_key(this);
  97. for (auto& key : Sector::get().get_objects_by_type<Key>()) {
  98. if (key.m_chain_pos > m_chain_pos)
  99. key.m_chain_pos -= 1;
  100. }
  101. door.unlock();
  102. m_my_door_pos = door.get_bbox().get_middle();
  103. m_unlock_timer.start(1.f);
  104. m_state = KeyState::FOUND;
  105. }
  106. }
  107. switch (m_state) {
  108. case NORMAL:
  109. break;
  110. case COLLECT:
  111. m_col.set_movement(Vector(0, 0));
  112. if (m_wait_timer.check())
  113. {
  114. m_wait_timer.stop();
  115. m_state = KeyState::FOLLOW;
  116. }
  117. break;
  118. case FOLLOW:
  119. m_total_time_elapsed += dt_sec;
  120. m_col.set_movement(((m_goal_pos - get_pos()) * (std::max(0.f, distance-(50.f*float(m_chain_pos)))/300.f))+
  121. Vector(0.f, m_target_speed * dt_sec));
  122. if (Sector::get().get_object_count<Player>([](const Player& player) { return !player.is_dead(); }) <= 0)
  123. m_state = KeyState::FOUND;
  124. break;
  125. case FOUND:
  126. m_col.set_movement((m_my_door_pos - Vector(get_bbox().get_width() / 2.f, get_bbox().get_height() / 2.f) -
  127. (get_pos()))
  128. *glm::length((m_my_door_pos - get_bbox().get_middle())*0.003f));
  129. if (m_unlock_timer.check() || m_owner->is_dying())
  130. {
  131. m_unlock_timer.stop();
  132. m_physic.enable_gravity(true);
  133. m_physic.set_velocity_y(-300.f);
  134. m_physic.set_acceleration_y(500.f);
  135. spawn_use_particles();
  136. m_state = KeyState::USE;
  137. }
  138. break;
  139. case USE:
  140. m_col.set_movement(m_physic.get_movement(dt_sec));
  141. if (get_pos().y > Sector::get().get_height())
  142. remove_me();
  143. break;
  144. }
  145. GameObject::update(dt_sec);
  146. }
  147. HitResponse
  148. Key::collision(MovingObject& other, const CollisionHit& hit_)
  149. {
  150. if (m_state == KeyState::NORMAL)
  151. {
  152. auto player = dynamic_cast<Player*>(&other);
  153. if (player)
  154. {
  155. spawn_use_particles();
  156. m_total_time_elapsed = 0.f;
  157. SoundManager::current()->play("sounds/metal_hit.ogg", get_pos());
  158. m_chain_pos = static_cast<int>(player->get_collected_keys().size()) + 1;
  159. player->add_collected_key(this);
  160. m_collected = true;
  161. m_wait_timer.start(0.5f);
  162. m_owner = player;
  163. m_state = KeyState::COLLECT;
  164. }
  165. }
  166. return FORCE_MOVE;
  167. }
  168. void
  169. Key::draw(DrawingContext& context)
  170. {
  171. if (m_owner && m_owner->is_dead())
  172. return;
  173. m_sprite->draw(context.color(), get_pos(), m_layer, m_flip);
  174. m_lightsprite->draw(context.light(), m_col.m_bbox.get_middle(), m_layer+1);
  175. }
  176. ObjectSettings
  177. Key::get_settings()
  178. {
  179. ObjectSettings result = MovingSprite::get_settings();
  180. result.add_color(_("Color"), &m_color, "color", Color::WHITE);
  181. result.reorder({ "color", "name", "x", "y" });
  182. return result;
  183. }
  184. void
  185. Key::after_editor_set()
  186. {
  187. MovingSprite::after_editor_set();
  188. m_lightsprite->set_blend(Blend::ADD);
  189. m_lightsprite->set_color(m_color);
  190. m_sprite->set_color(m_color);
  191. }
  192. void
  193. Key::update_pos()
  194. {
  195. m_col.m_bbox.set_pos(m_owner->get_bbox().get_middle() -
  196. Vector(m_col.m_bbox.get_width() / 2.f, m_col.m_bbox.get_height() / 2.f - 10.f));
  197. }
  198. void
  199. Key::spawn_use_particles()
  200. {
  201. for (int i = 1; i < 9; i++)
  202. {
  203. Vector direction = glm::normalize(Vector(std::cos(float(i) * math::PI_4), std::sin(float(i) * math::PI_4)));
  204. Sector::get().add<SpriteParticle>("images/particles/sparkle.sprite", "small-key-collect",
  205. get_bbox().get_middle(),
  206. ANCHOR_MIDDLE, Vector(400.f * direction), -Vector(400.f * direction) * 2.8f, LAYER_OBJECTS + 6, false, m_color);
  207. }
  208. }
  209. /* EOF */