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