1 /*
2  * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
3  *
4  * Solarus 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  * Solarus 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 along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "solarus/audio/Sound.h"
18 #include "solarus/core/Game.h"
19 #include "solarus/core/GameCommands.h"
20 #include "solarus/core/Geometry.h"
21 #include "solarus/core/Map.h"
22 #include "solarus/core/System.h"
23 #include "solarus/entities/Enemy.h"
24 #include "solarus/hero/FreeState.h"
25 #include "solarus/hero/HeroSprites.h"
26 #include "solarus/hero/SwordTappingState.h"
27 #include "solarus/hero/SwordLoadingState.h"
28 #include "solarus/movements/StraightMovement.h"
29 #include <memory>
30 #include <string>
31 
32 namespace Solarus {
33 
34 /**
35  * \brief Constructor.
36  * \param hero The hero controlled by this state.
37  */
SwordTappingState(Hero & hero)38 Hero::SwordTappingState::SwordTappingState(Hero& hero):
39   HeroState(hero, "sword tapping"),
40   next_sound_date(0) {
41 
42 }
43 
44 /**
45  * \brief Starts this state.
46  * \param previous_state the previous state
47  */
start(const State * previous_state)48 void Hero::SwordTappingState::start(const State* previous_state) {
49 
50   HeroState::start(previous_state);
51 
52   get_sprites().set_animation_sword_tapping();
53   next_sound_date = System::now() + 100;
54 }
55 
56 /**
57  * \brief Ends this state.
58  * \param next_state the next state
59  */
stop(const State * next_state)60 void Hero::SwordTappingState::stop(const State* next_state) {
61 
62   HeroState::stop(next_state);
63 
64   Hero& hero = get_entity();
65   if (hero.get_movement() != nullptr) {
66     // stop the movement of being pushed by an enemy after hitting him
67     hero.clear_movement();
68   }
69 }
70 
71 /**
72  * \brief Updates this state.
73  */
update()74 void Hero::SwordTappingState::update() {
75 
76   HeroState::update();
77 
78   if (is_suspended()) {
79     return;
80   }
81 
82   Hero& hero = get_entity();
83   if (hero.get_movement() == nullptr) {
84     // the hero is not being pushed after hitting an enemy
85 
86     const Point& facing_point = hero.get_facing_point();
87 
88     if (!get_commands().is_command_pressed(GameCommand::ATTACK)
89         || get_commands().get_wanted_direction8() != get_sprites().get_animation_direction8()
90         || !get_map().test_collision_with_obstacles(hero.get_layer(), facing_point, hero)) {
91       // the sword key has been released, the player has moved or the obstacle is gone
92 
93       if (get_sprites().get_current_frame() >= 5) {
94         // when the animation is ok, stop tapping the wall, go back to loading the sword
95         hero.set_state(std::make_shared<SwordLoadingState>(hero, 1000));
96       }
97     }
98     else {
99 
100       // play the sound every 100 ms
101       uint32_t now = System::now();
102       if (get_sprites().get_current_frame() == 3 && now >= next_sound_date) {
103 
104         Entity* facing_entity = hero.get_facing_entity();
105         std::string sound_id;
106         if (facing_entity != nullptr) {
107           sound_id = facing_entity->get_sword_tapping_sound();
108         }
109         else {
110           sound_id = "sword_tapping";
111         }
112         Sound::play(sound_id);
113         next_sound_date = now + 100;
114       }
115     }
116   }
117   else if (hero.get_movement()->is_finished()) {
118     // the hero was pushed by an enemy
119     hero.set_state(std::make_shared<FreeState>(hero));
120   }
121 }
122 
123 /**
124  * \brief Notifies this state that the game was just suspended or resumed.
125  * \param suspended true if the game is suspended
126  */
set_suspended(bool suspended)127 void Hero::SwordTappingState::set_suspended(bool suspended) {
128 
129   HeroState::set_suspended(suspended);
130 
131   if (!suspended) {
132     next_sound_date += System::now() - get_when_suspended();
133   }
134 }
135 
136 /**
137  * \brief Returns whether crystals can be activated by the sword in this state.
138  * \return true if crystals can be activated by the sword in this state
139  */
can_sword_hit_crystal() const140 bool Hero::SwordTappingState::can_sword_hit_crystal() const {
141   return true;
142 }
143 
144 /**
145  * \brief Returns whether the hero can pick a treasure in this state.
146  * \param item The equipment item to obtain.
147  * \return true if the hero can pick that treasure in this state.
148  */
get_can_pick_treasure(EquipmentItem &) const149 bool Hero::SwordTappingState::get_can_pick_treasure(EquipmentItem& /* item */) const {
150   return true;
151 }
152 
153 /**
154  * \copydoc Entity::State::can_use_shield
155  */
get_can_use_shield() const156 bool Hero::SwordTappingState::get_can_use_shield() const {
157   return false;
158 }
159 
160 /**
161  * \copydoc Entity::State::is_cutting_with_sword
162  */
is_cutting_with_sword(Entity & entity)163 bool Hero::SwordTappingState::is_cutting_with_sword(Entity& entity) {
164 
165   Hero& hero = get_entity();
166   return entity.is_obstacle_for(hero)         // only obstacle entities can be cut
167     && hero.get_facing_entity() == &entity    // only one entity at a time
168     && get_sprites().get_current_frame() >= 3;  // wait until the animation shows an appropriate frame
169 }
170 
171 /**
172  * \brief Returns whether a teletransporter is considered as an obstacle in this state.
173  * \param teletransporter a teletransporter
174  * \return true if the teletransporter is an obstacle in this state
175  */
is_teletransporter_obstacle(Teletransporter &)176 bool Hero::SwordTappingState::is_teletransporter_obstacle(
177     Teletransporter& /* teletransporter */) {
178 
179   // if the hero was pushed by an enemy, don't go on a teletransporter
180   return get_entity().get_movement() != nullptr;
181 }
182 
183 /**
184  * \brief Notifies this state that the hero has just failed to change its
185  * position because of obstacles.
186  */
notify_obstacle_reached()187 void Hero::SwordTappingState::notify_obstacle_reached() {
188 
189   // the hero reached an obstacle while being pushed after hitting an enemy
190   Hero& hero = get_entity();
191   hero.clear_movement();
192   hero.set_state(std::make_shared<FreeState>(hero));
193 }
194 
195 /**
196  * \copydoc Entity::State::notify_attacked_enemy
197  */
notify_attacked_enemy(EnemyAttack attack,Enemy & victim,Sprite * victim_sprite,const EnemyReaction::Reaction & result,bool)198 void Hero::SwordTappingState::notify_attacked_enemy(
199     EnemyAttack attack,
200     Enemy& victim,
201     Sprite* victim_sprite,
202     const EnemyReaction::Reaction& result,
203     bool /* killed */) {
204 
205   if (attack == EnemyAttack::SWORD &&
206       victim.get_push_hero_on_sword() &&
207       result.type != EnemyReaction::ReactionType::IGNORED &&
208       result.type != EnemyReaction::ReactionType::LUA_CALLBACK) {
209 
210     Hero& hero = get_entity();
211     double angle = victim.get_angle(hero, victim_sprite, nullptr);
212     std::shared_ptr<StraightMovement> movement =
213         std::make_shared<StraightMovement>(false, true);
214     movement->set_max_distance(24);
215     movement->set_speed(120);
216     movement->set_angle(angle);
217     hero.set_movement(movement);
218     get_sprites().set_animation_walking_normal();
219   }
220 }
221 
222 }
223 
224