rock.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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/rock.hpp"
  17. #include "audio/sound_manager.hpp"
  18. #include "badguy/crusher.hpp"
  19. #include "badguy/badguy.hpp"
  20. #include "object/coin.hpp"
  21. #include "object/explosion.hpp"
  22. #include "object/lit_object.hpp"
  23. #include "object/pushbutton.hpp"
  24. #include "object/trampoline.hpp"
  25. #include "supertux/constants.hpp"
  26. #include "supertux/sector.hpp"
  27. #include "supertux/tile.hpp"
  28. #include "object/player.hpp"
  29. #include "util/reader_mapping.hpp"
  30. namespace {
  31. const std::string ROCK_SOUND = "sounds/brick.wav"; //TODO use own sound.
  32. const float GROUND_FRICTION = 0.1f; // Amount of friction to apply while on ground.
  33. } // namespace
  34. Rock::Rock(const ReaderMapping& reader, const std::string& spritename) :
  35. MovingSprite(reader, spritename),
  36. physic(),
  37. on_ground(false),
  38. on_ice(false),
  39. last_movement(0.0f, 0.0f),
  40. on_grab_script(),
  41. on_ungrab_script(),
  42. running_grab_script(),
  43. running_ungrab_script()
  44. {
  45. parse_type(reader);
  46. reader.get("on-grab-script", on_grab_script, "");
  47. reader.get("on-ungrab-script", on_ungrab_script, "");
  48. SoundManager::current()->preload(ROCK_SOUND);
  49. set_group(COLGROUP_MOVING_STATIC);
  50. }
  51. Rock::Rock(const Vector& pos, const std::string& spritename) :
  52. MovingSprite(pos, spritename),
  53. physic(),
  54. on_ground(false),
  55. on_ice(false),
  56. last_movement(0.0f, 0.0f),
  57. on_grab_script(),
  58. on_ungrab_script(),
  59. running_grab_script(),
  60. running_ungrab_script()
  61. {
  62. SoundManager::current()->preload(ROCK_SOUND);
  63. set_group(COLGROUP_MOVING_STATIC);
  64. }
  65. GameObjectTypes
  66. Rock::get_types() const
  67. {
  68. return {
  69. { "small", _("Small") },
  70. { "large", _("Large") }
  71. };
  72. }
  73. std::string
  74. Rock::get_default_sprite_name() const
  75. {
  76. switch (m_type)
  77. {
  78. case LARGE:
  79. return "images/objects/rock/rock-b.png";
  80. default:
  81. return m_default_sprite_name;
  82. }
  83. }
  84. void
  85. Rock::update(float dt_sec)
  86. {
  87. if (!is_grabbed()) {
  88. if (get_bbox().get_top() > Sector::get().get_height()) {
  89. remove_me();
  90. }
  91. Rectf icebox = get_bbox().grown(-1.f);
  92. icebox.set_bottom(get_bbox().get_bottom() + 8.f);
  93. on_ice = !Sector::get().is_free_of_tiles(icebox, true, Tile::ICE);
  94. bool in_water = !Sector::get().is_free_of_tiles(get_bbox(), true, Tile::WATER);
  95. physic.set_gravity_modifier(in_water ? 0.2f : 1.f);
  96. Rectf trampolinebox = get_bbox().grown(-1.f);
  97. trampolinebox.set_bottom(get_bbox().get_bottom() + 8.f);
  98. for (auto& trampoline : Sector::get().get_objects_by_type<Trampoline>()) {
  99. if (trampolinebox.overlaps(trampoline.get_bbox()) && !trampoline.is_grabbed() &&
  100. (glm::length((get_bbox().get_middle() - trampoline.get_bbox().get_middle())) >= 10.f) &&
  101. is_portable()) {
  102. trampoline.bounce();
  103. physic.set_velocity_y(-500.f);
  104. }
  105. }
  106. Rectf playerbox = get_bbox().grown(-2.f);
  107. playerbox.set_bottom(get_bbox().get_bottom() + 7.f);
  108. for (auto& player : Sector::get().get_objects_by_type<Player>()) {
  109. if (playerbox.overlaps(player.get_bbox()) && physic.get_velocity_y() > 0.f && is_portable()) {
  110. physic.set_velocity_y(-250.f);
  111. }
  112. }
  113. m_col.set_movement(physic.get_movement(dt_sec) *
  114. Vector(in_water ? 0.4f : 1.f, in_water ? 0.6f : 1.f));
  115. }
  116. }
  117. void
  118. Rock::collision_solid(const CollisionHit& hit)
  119. {
  120. if (is_grabbed()) {
  121. return;
  122. }
  123. if (hit.top || hit.bottom)
  124. physic.set_velocity_y(0);
  125. if (hit.left || hit.right) {
  126. // Bounce back slightly when hitting a wall
  127. float velx = physic.get_velocity_x();
  128. physic.set_velocity_x(-0.1f * velx);
  129. }
  130. if (hit.crush)
  131. physic.set_velocity(0, 0);
  132. if (hit.bottom && !on_ground && !is_grabbed() && !on_ice) {
  133. SoundManager::current()->play(ROCK_SOUND, get_pos());
  134. physic.set_velocity_x(0);
  135. on_ground = true;
  136. }
  137. if (on_ground || (hit.bottom && on_ice)) {
  138. // Full friction!
  139. physic.set_velocity_x(physic.get_velocity_x() * (1.f - (GROUND_FRICTION * (on_ice ? 0.5f : 1.f))));
  140. }
  141. }
  142. HitResponse
  143. Rock::collision(GameObject& other, const CollisionHit& hit)
  144. {
  145. auto heavy_coin = dynamic_cast<HeavyCoin*> (&other);
  146. if (heavy_coin) {
  147. return ABORT_MOVE;
  148. }
  149. auto explosion = dynamic_cast<Explosion*> (&other);
  150. if (explosion) {
  151. return ABORT_MOVE;
  152. }
  153. // Why is it necessary to list exceptions here? Why doesn't the rock just not
  154. // affect object that have ABORT_MOVE on all collisions?
  155. auto litobject = dynamic_cast<LitObject*> (&other);
  156. if (litobject) {
  157. return ABORT_MOVE;
  158. }
  159. auto pushbutton = dynamic_cast<PushButton*> (&other);
  160. if (pushbutton) {
  161. return ABORT_MOVE;
  162. }
  163. if (is_grabbed()) {
  164. return ABORT_MOVE;
  165. }
  166. auto crusher = dynamic_cast<Crusher*> (&other);
  167. if (crusher) {
  168. return FORCE_MOVE;
  169. }
  170. if (hit.bottom) {
  171. auto player = dynamic_cast<Player*> (&other);
  172. if (player) {
  173. physic.set_velocity_y(-250.f);
  174. }
  175. }
  176. // Don't fall further if we are on a rock which is on the ground.
  177. // This is to avoid jittering.
  178. auto rock = dynamic_cast<Rock*> (&other);
  179. if (rock && rock->on_ground && hit.bottom) {
  180. physic.set_velocity_y(rock->get_physic().get_velocity_y());
  181. return CONTINUE;
  182. }
  183. if (!on_ground) {
  184. if (hit.bottom && physic.get_velocity_y() > 200) {
  185. auto badguy = dynamic_cast<BadGuy*> (&other);
  186. if (badguy && badguy->get_group() != COLGROUP_TOUCHABLE) {
  187. //Getting a rock on the head hurts. A lot.
  188. badguy->kill_fall();
  189. physic.set_velocity_y(0);
  190. }
  191. }
  192. return FORCE_MOVE;
  193. }
  194. return FORCE_MOVE;
  195. }
  196. void
  197. Rock::grab(MovingObject& object, const Vector& pos, Direction dir_)
  198. {
  199. Portable::grab(object, pos, dir_);
  200. Vector movement = pos - get_pos();
  201. m_col.set_movement(movement);
  202. physic.set_velocity(movement * LOGICAL_FPS);
  203. last_movement = movement;
  204. set_group(COLGROUP_TOUCHABLE); //needed for lanterns catching willowisps
  205. on_ground = false;
  206. running_ungrab_script = false;
  207. if (!on_grab_script.empty() && !running_grab_script)
  208. {
  209. running_grab_script = true;
  210. Sector::get().run_script(on_grab_script, "Rock::on_grab");
  211. }
  212. }
  213. void
  214. Rock::ungrab(MovingObject& object, Direction dir)
  215. {
  216. auto player = dynamic_cast<Player*> (&object);
  217. set_group(COLGROUP_MOVING_STATIC);
  218. on_ground = false;
  219. if (player)
  220. {
  221. if (player->is_swimming() || player->is_water_jumping())
  222. {
  223. float swimangle = player->get_swimming_angle();
  224. physic.set_velocity(player->get_velocity() + Vector(std::cos(swimangle), std::sin(swimangle)));
  225. }
  226. else
  227. {
  228. physic.set_velocity_x(fabsf(player->get_physic().get_velocity_x()) < 1.f ? 0.f :
  229. player->m_dir == Direction::LEFT ? -200.f : 200.f);
  230. physic.set_velocity_y((dir == Direction::UP) ? -500.f : (dir == Direction::DOWN) ? 500.f :
  231. (glm::length(last_movement) > 1) ? -200.f : 0.f);
  232. }
  233. }
  234. running_grab_script = false;
  235. if (!on_ungrab_script.empty() && !running_ungrab_script)
  236. {
  237. running_ungrab_script = true;
  238. Sector::get().run_script(on_ungrab_script, "Rock::on_ungrab");
  239. }
  240. Portable::ungrab(object, dir);
  241. }
  242. void
  243. Rock::draw(DrawingContext& context)
  244. {
  245. Vector offset = physic.get_velocity() * context.get_time_offset();
  246. m_sprite->draw(context.color(), get_pos() + offset, m_layer, m_flip);
  247. }
  248. ObjectSettings
  249. Rock::get_settings()
  250. {
  251. auto result = MovingSprite::get_settings();
  252. result.add_script(_("On-grab script"), &on_grab_script, "on-grab-script");
  253. result.add_script(_("On-ungrab script"), &on_ungrab_script, "on-ungrab-script");
  254. return result;
  255. }
  256. void
  257. Rock::add_wind_velocity(const Vector& velocity, const Vector& end_speed)
  258. {
  259. // only add velocity in the same direction as the wind
  260. if (end_speed.x > 0 && physic.get_velocity_x() < end_speed.x)
  261. physic.set_velocity_x(std::min(physic.get_velocity_x() + velocity.x, end_speed.x));
  262. if (end_speed.x < 0 && physic.get_velocity_x() > end_speed.x)
  263. physic.set_velocity_x(std::max(physic.get_velocity_x() + velocity.x, end_speed.x));
  264. if (end_speed.y > 0 && physic.get_velocity_y() < end_speed.y)
  265. physic.set_velocity_y(std::min(physic.get_velocity_y() + velocity.y, end_speed.y));
  266. if (end_speed.y < 0 && physic.get_velocity_y() > end_speed.y)
  267. physic.set_velocity_y(std::max(physic.get_velocity_y() + velocity.y, end_speed.y));
  268. }
  269. /* EOF */