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/core/Equipment.h"
18 #include "solarus/core/Game.h"
19 #include "solarus/core/GameCommands.h"
20 #include "solarus/entities/Enemy.h"
21 #include "solarus/hero/FreeState.h"
22 #include "solarus/hero/HeroSprites.h"
23 #include "solarus/hero/SwordLoadingState.h"
24 #include "solarus/hero/SwordSwingingState.h"
25 #include "solarus/movements/StraightMovement.h"
26 #include <memory>
27 
28 namespace Solarus {
29 
30 /**
31  * \brief Constructor.
32  * \param hero The hero controlled by this state.
33  */
SwordSwingingState(Hero & hero)34 Hero::SwordSwingingState::SwordSwingingState(Hero& hero):
35   HeroState(hero, "sword swinging"),
36   attacked(false),
37   sword_finished(false) {
38 
39 }
40 
41 /**
42  * \brief Starts this state.
43  * \param previous_state the previous state
44  */
start(const State * previous_state)45 void Hero::SwordSwingingState::start(const State* previous_state) {
46 
47   HeroState::start(previous_state);
48 
49   get_sprites().play_sword_sound();
50   get_sprites().set_animation_sword();
51   get_equipment().notify_ability_used(Ability::SWORD);
52 }
53 
54 /**
55  * \brief Ends this state.
56  * \param next_state the next state
57  */
stop(const State * next_state)58 void Hero::SwordSwingingState::stop(const State* next_state) {
59 
60   HeroState::stop(next_state);
61 
62   Hero& hero = get_entity();
63   if (hero.get_movement() != nullptr) {
64     // stop the movement of being pushed by an enemy after hitting him
65     hero.clear_movement();
66   }
67 }
68 
69 /**
70  * \brief Updates this state.
71  */
update()72 void Hero::SwordSwingingState::update() {
73 
74   HeroState::update();
75 
76   // check the animation
77   Hero& hero = get_entity();
78   if (get_sprites().is_animation_finished()) {
79 
80     sword_finished = true;
81     if (hero.get_movement() == nullptr) {
82 
83       // if the player is still pressing the sword key, start loading the sword
84       if (get_commands().is_command_pressed(GameCommand::ATTACK)
85           && !attacked) {
86         hero.set_state(std::make_shared<SwordLoadingState>(hero, 1000));
87       }
88       else {
89         hero.set_state(std::make_shared<FreeState>(hero));
90       }
91     }
92     else {
93       // the sword animation is finished, but the movement continues
94       hero.get_hero_sprites().set_animation_stopped_normal();
95     }
96   }
97 
98   // check the movement if any
99   if (hero.get_movement() != nullptr && hero.get_movement()->is_finished()) {
100     hero.clear_movement();
101     if (sword_finished) {
102       hero.set_state(std::make_shared<FreeState>(hero));
103     }
104   }
105 }
106 
107 /**
108  * \brief Returns whether the hero can swing his sword in this state.
109  * \return true if the hero can swing his sword in this state
110  */
get_can_start_sword() const111 bool Hero::SwordSwingingState::get_can_start_sword() const {
112   return get_entity().get_movement() == nullptr;
113 }
114 
115 /**
116  * \brief Returns whether the hero can be hurt in this state.
117  * \return true if the hero can be hurt in this state
118  * \param attacker an attacker that is trying to hurt the hero
119  * (or nullptr if the source of the attack is not an enemy)
120  */
get_can_be_hurt(Entity *)121 bool Hero::SwordSwingingState::get_can_be_hurt(Entity* /* attacker */) {
122   return true;
123 }
124 
125 /**
126  * \brief Returns whether the hero can pick a treasure in this state.
127  * \param item The equipment item to obtain.
128  * \return true if the hero can pick that treasure in this state.
129  */
get_can_pick_treasure(EquipmentItem &) const130 bool Hero::SwordSwingingState::get_can_pick_treasure(EquipmentItem& /* item */) const {
131   return true;
132 }
133 
134 /**
135  * \copydoc Entity::State::can_use_shield
136  */
get_can_use_shield() const137 bool Hero::SwordSwingingState::get_can_use_shield() const {
138   return false;
139 }
140 
141 /**
142  * \brief Returns whether crystals can be activated by the sword in this state.
143  * \return true if crystals can be activated by the sword in this state
144  */
can_sword_hit_crystal() const145 bool Hero::SwordSwingingState::can_sword_hit_crystal() const {
146   return true;
147 }
148 
149 /**
150  * \copydoc Entity::State::is_cutting_with_sword
151  */
is_cutting_with_sword(Entity & entity)152 bool Hero::SwordSwingingState::is_cutting_with_sword(
153     Entity& entity) {
154 
155   Hero& hero = get_entity();
156   if (hero.get_movement() != nullptr) {
157     return false;
158   }
159 
160   // check the distance to the detector
161   int distance = entity.is_obstacle_for(hero) ? 14 : 4;
162   Point tested_point = hero.get_facing_point();
163 
164   switch (get_sprites().get_animation_direction()) {
165 
166     case 0: // right
167       tested_point.x += distance;
168       break;
169 
170     case 1: // up
171       tested_point.y -= distance;
172       break;
173 
174     case 2: // left
175       tested_point.x -= distance;
176       break;
177 
178     case 3: // down
179       tested_point.y += distance;
180       break;
181   }
182 
183   return entity.overlaps(tested_point);
184 }
185 
186 /**
187  * \brief Returns whether a teletransporter is considered as an obstacle in this state.
188  * \param teletransporter a teletransporter
189  * \return true if the teletransporter is an obstacle in this state
190  */
is_teletransporter_obstacle(Teletransporter &)191 bool Hero::SwordSwingingState::is_teletransporter_obstacle(
192     Teletransporter& /* teletransporter */) {
193 
194   // if the hero was pushed by an enemy, don't go on a teletransporter
195   return get_entity().get_movement() != nullptr;
196 }
197 
198 /**
199  * \brief Notifies this state that the hero has just failed to change its
200  * position because of obstacles.
201  */
notify_obstacle_reached()202 void Hero::SwordSwingingState::notify_obstacle_reached() {
203 
204   // the hero reached an obstacle while being pushed after hitting an enemy
205   Hero& hero = get_entity();
206   hero.clear_movement();
207 
208   if (sword_finished) {
209     hero.set_state(std::make_shared<FreeState>(hero));
210   }
211 }
212 
213 /**
214  * \copydoc Entity::State::notify_attacked_enemy
215  */
notify_attacked_enemy(EnemyAttack attack,Enemy & victim,Sprite * victim_sprite,const EnemyReaction::Reaction & result,bool)216 void Hero::SwordSwingingState::notify_attacked_enemy(
217     EnemyAttack attack,
218     Enemy& victim,
219     Sprite* victim_sprite,
220     const EnemyReaction::Reaction& result,
221     bool /* killed */) {
222 
223   if (attack == EnemyAttack::SWORD &&
224       result.type != EnemyReaction::ReactionType::IGNORED &&
225       result.type != EnemyReaction::ReactionType::LUA_CALLBACK) {
226     attacked = true;
227 
228     if (victim.get_push_hero_on_sword()) {
229 
230       Hero& hero = get_entity();
231       double angle = victim.get_angle(hero, victim_sprite, nullptr);
232       std::shared_ptr<StraightMovement> movement =
233           std::make_shared<StraightMovement>(false, true);
234       movement->set_max_distance(24);
235       movement->set_speed(120);
236       movement->set_angle(angle);
237       hero.set_movement(movement);
238     }
239   }
240 }
241 
242 }
243 
244