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