door.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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 "trigger/door.hpp"
  17. #include "audio/sound_manager.hpp"
  18. #include "math/random.hpp"
  19. #include "object/player.hpp"
  20. #include "object/sprite_particle.hpp"
  21. #include "sprite/sprite.hpp"
  22. #include "sprite/sprite_manager.hpp"
  23. #include "supertux/fadetoblack.hpp"
  24. #include "supertux/game_session.hpp"
  25. #include "supertux/screen_manager.hpp"
  26. #include "supertux/sector.hpp"
  27. #include "supertux/flip_level_transformer.hpp"
  28. #include "util/reader_mapping.hpp"
  29. static const float STAY_OPEN_TIME = 1.0f;
  30. static const float LOCK_WARN_TIME = 0.5f;
  31. static const float CENTER_EPSILON = 5.0f;
  32. static const float WALK_SPEED = 100.0f;
  33. Door::Door(const ReaderMapping& mapping) :
  34. SpritedTrigger(mapping, "images/objects/door/door.sprite"),
  35. m_state(CLOSED),
  36. m_target_sector(),
  37. m_target_spawnpoint(),
  38. m_script(),
  39. m_lock_sprite(SpriteManager::current()->create("images/objects/door/door_lock.sprite")),
  40. m_stay_open_timer(),
  41. m_unlocking_timer(),
  42. m_lock_warn_timer(),
  43. m_locked(),
  44. m_lock_color(Color::WHITE),
  45. m_transition_triggered(false),
  46. m_triggering_player(nullptr)
  47. {
  48. mapping.get("sector", m_target_sector);
  49. mapping.get("spawnpoint", m_target_spawnpoint);
  50. mapping.get("script", m_script);
  51. mapping.get("locked", m_locked);
  52. m_state = m_locked ? DoorState::LOCKED : DoorState::CLOSED;
  53. set_action("closed");
  54. std::vector<float> vColor;
  55. if (mapping.get("lock-color", vColor))
  56. m_lock_color = Color(vColor);
  57. else
  58. m_lock_color = Color::WHITE;
  59. m_lock_sprite->set_color(m_lock_color);
  60. SoundManager::current()->preload("sounds/door.wav");
  61. // TODO: Add proper sounds.
  62. SoundManager::current()->preload("sounds/locked.ogg");
  63. SoundManager::current()->preload("sounds/turnkey.ogg");
  64. }
  65. ObjectSettings
  66. Door::get_settings()
  67. {
  68. ObjectSettings result = SpritedTrigger::get_settings();
  69. result.add_script(_("Script"), &m_script, "script");
  70. result.add_text(_("Sector"), &m_target_sector, "sector");
  71. result.add_text(_("Spawn point"), &m_target_spawnpoint, "spawnpoint");
  72. result.add_bool(_("Locked?"), &m_locked, "locked");
  73. result.add_color(_("Lock Color"), &m_lock_color, "lock-color", Color::WHITE);
  74. result.reorder({"sector", "lock-color", "locked", "spawnpoint", "name", "x", "y"});
  75. return result;
  76. }
  77. void
  78. Door::after_editor_set()
  79. {
  80. SpritedTrigger::after_editor_set();
  81. m_lock_sprite->set_color(m_lock_color);
  82. }
  83. void
  84. Door::update(float )
  85. {
  86. switch (m_state) {
  87. case CLOSED:
  88. m_transition_triggered = false;
  89. break;
  90. case OPENING:
  91. // If door has finished opening, start timer and keep door open.
  92. if (m_sprite->animation_done()) {
  93. m_state = OPEN;
  94. set_action("open");
  95. m_stay_open_timer.start(STAY_OPEN_TIME);
  96. m_transition_triggered = false;
  97. }
  98. break;
  99. case OPEN:
  100. // If door was open long enough, start closing it.
  101. if (m_stay_open_timer.check()) {
  102. m_state = CLOSING;
  103. set_action("closing", 1);
  104. }
  105. break;
  106. case CLOSING:
  107. // If door has finished closing, keep it shut.
  108. if (m_sprite->animation_done()) {
  109. m_state = CLOSED;
  110. set_action("closed");
  111. }
  112. break;
  113. case LOCKED:
  114. if (m_lock_warn_timer.check()) {
  115. m_lock_warn_timer.stop();
  116. }
  117. break;
  118. case UNLOCKING:
  119. if (m_unlocking_timer.check())
  120. {
  121. Sector::get().add<SpriteParticle>("images/objects/door/door_lock.sprite",
  122. "default", get_bbox().get_middle(), ANCHOR_MIDDLE, Vector(0.f, -300.f), Vector(0.f, 1000.f), LAYER_OBJECTS - 2, true, m_lock_color);
  123. m_unlocking_timer.stop();
  124. m_state = DoorState::CLOSED;
  125. }
  126. break;
  127. }
  128. if (m_triggering_player)
  129. {
  130. // Check if Tux should move a bit closer to the door so that he could go through smoothly
  131. const Vector diff_to_center = get_bbox().get_middle() - m_triggering_player->get_bbox().get_middle();
  132. if (fabs(diff_to_center.x) >= CENTER_EPSILON)
  133. {
  134. const bool move_right = diff_to_center.x > 0.0f;
  135. m_triggering_player->set_dir(move_right);
  136. m_triggering_player->walk(move_right ? WALK_SPEED : -WALK_SPEED);
  137. }
  138. else
  139. {
  140. m_triggering_player->walk(0.0f);
  141. m_triggering_player = nullptr;
  142. }
  143. }
  144. }
  145. void
  146. Door::draw(DrawingContext& context)
  147. {
  148. m_sprite->draw(context.color(), m_col.m_bbox.p1(), LAYER_BACKGROUNDTILES+1, m_flip);
  149. if (m_state == DoorState::LOCKED || m_state == DoorState::UNLOCKING)
  150. {
  151. Vector shake_delta = Vector(static_cast<float>(graphicsRandom.rand(-8, 8)), static_cast<float>(graphicsRandom.rand(-8, 8)));
  152. float shake_strength = m_lock_warn_timer.started() ? m_lock_warn_timer.get_timeleft() : 0.f;
  153. m_lock_sprite->draw(context.color(), get_bbox().get_middle() -
  154. (Vector(m_lock_sprite->get_width() / 2, m_lock_sprite->get_height() / 2) + (shake_delta*shake_strength)), LAYER_BACKGROUNDTILES + 1, m_flip);
  155. }
  156. }
  157. void
  158. Door::event(Player& , EventType type)
  159. {
  160. switch (m_state) {
  161. case CLOSED:
  162. // If door was activated, start opening it.
  163. if (type == EVENT_ACTIVATE) {
  164. m_state = OPENING;
  165. SoundManager::current()->play("sounds/door.wav", get_pos());
  166. set_action("opening", 1);
  167. }
  168. break;
  169. case OPENING:
  170. break;
  171. case OPEN:
  172. break;
  173. case CLOSING:
  174. break;
  175. case LOCKED:
  176. SoundManager::current()->play("sounds/locked.ogg", get_pos());
  177. m_lock_warn_timer.start(LOCK_WARN_TIME);
  178. break;
  179. case UNLOCKING:
  180. m_state = CLOSED;
  181. break;
  182. }
  183. }
  184. HitResponse
  185. Door::collision(GameObject& other, const CollisionHit& hit_)
  186. {
  187. switch (m_state) {
  188. case CLOSED:
  189. break;
  190. case OPENING:
  191. {
  192. // If door is opening and was touched by a player, teleport the player.
  193. Player* player = dynamic_cast<Player*>(&other);
  194. if (player)
  195. {
  196. if (!m_transition_triggered)
  197. {
  198. m_transition_triggered = true;
  199. m_triggering_player = player;
  200. if (!m_script.empty()) {
  201. Sector::get().run_script(m_script, "Door");
  202. }
  203. if (!m_target_sector.empty())
  204. {
  205. // Disable controls, GameSession will make safe Tux during fade animation.
  206. // Controls will be reactivated after spawn
  207. m_triggering_player->deactivate();
  208. GameSession::current()->respawn_with_fade(m_target_sector,
  209. m_target_spawnpoint,
  210. ScreenFade::FadeType::CIRCLE,
  211. get_bbox().get_middle(),
  212. true);
  213. }
  214. }
  215. }
  216. break;
  217. }
  218. case OPEN:
  219. case CLOSING:
  220. case LOCKED:
  221. case UNLOCKING:
  222. break;
  223. }
  224. return TriggerBase::collision(other, hit_);
  225. }
  226. void
  227. Door::on_flip(float height)
  228. {
  229. MovingObject::on_flip(height);
  230. FlipLevelTransformer::transform_flip(m_flip);
  231. }
  232. void
  233. Door::unlock()
  234. {
  235. m_locked = false;
  236. SoundManager::current()->play("sounds/turnkey.ogg", get_pos());
  237. m_unlocking_timer.start(1.f);
  238. m_state = DoorState::UNLOCKING;
  239. }
  240. /* EOF */