1// SuperTux
2// Copyright (C) 2008 Wolfgang Becker <uafr@gmx.de>
3// Copyright (C) 2010 Florian Forster <supertux at octo.it>
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18#include "badguy/owl.hpp"
19
20#include "audio/sound_manager.hpp"
21#include "editor/editor.hpp"
22#include "object/player.hpp"
23#include "object/portable.hpp"
24#include "sprite/sprite.hpp"
25#include "supertux/game_object_factory.hpp"
26#include "supertux/sector.hpp"
27#include "util/reader_mapping.hpp"
28#include "util/writer.hpp"
29
30namespace {
31
32const float FLYING_SPEED = 120.0f;
33const float ACTIVATION_DISTANCE = 128.0f;
34
35} // namespace
36
37Owl::Owl(const ReaderMapping& reader) :
38 BadGuy(reader, "images/creatures/owl/owl.sprite", LAYER_OBJECTS + 1),
39 carried_obj_name(),
40 carried_object(nullptr)
41{
42 reader.get("carry", carried_obj_name, "skydive");
43 set_action (m_dir == Direction::LEFT ? "left" : "right", /* loops = */ -1);
44}
45
46void
47Owl::initialize()
48{
49 m_physic.set_velocity_x(m_dir == Direction::LEFT ? -FLYING_SPEED : FLYING_SPEED);
50 m_physic.enable_gravity(false);
51 m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right");
52
53 // If we add the carried object to the sector while we're editing
54 // a level with the editor, it gets written to the level file,
55 // resulting in two carried objects. Returning early is much better.
56 if (Editor::is_active())
57 {
58 return;
59 }
60
61 auto game_object = GameObjectFactory::instance().create(carried_obj_name, get_pos(), m_dir);
62 if (game_object == nullptr)
63 {
64 log_fatal << "Creating \"" << carried_obj_name << "\" object failed." << std::endl;
65 }
66 else
67 {
68 carried_object = dynamic_cast<Portable*>(game_object.get());
69 if (carried_object == nullptr)
70 {
71 log_warning << "Object is not portable: " << carried_obj_name << std::endl;
72 }
73 else
74 {
75 Sector::get().add_object(std::move(game_object));
76 }
77 }
78}
79
80bool
81Owl::is_above_player() const
82{
83 auto player = Sector::get().get_nearest_player(m_col.m_bbox);
84 if (!player)
85 return false;
86
87 // Let go of carried objects a short while *before* Tux is below us. This
88 // makes it more likely that we'll hit him.
89 float x_offset = (m_dir == Direction::LEFT) ? ACTIVATION_DISTANCE : -ACTIVATION_DISTANCE;
90
91 const Rectf& player_bbox = player->get_bbox();
92
93 return ((player_bbox.get_top() >= m_col.m_bbox.get_bottom()) /* player is below us */
94 && ((player_bbox.get_right() + x_offset) > m_col.m_bbox.get_left())
95 && ((player_bbox.get_left() + x_offset) < m_col.m_bbox.get_right()));
96}
97
98void
99Owl::active_update (float dt_sec)
100{
101 BadGuy::active_update (dt_sec);
102
103 if (m_frozen)
104 return;
105
106 if (carried_object != nullptr) {
107 if (!is_above_player ()) {
108 Vector obj_pos = get_anchor_pos(m_col.m_bbox, ANCHOR_BOTTOM);
109 obj_pos.x -= 16.f; /* FIXME: Actually do use the half width of the carried object here. */
110 obj_pos.y += 3.f; /* Move a little away from the hitbox (the body). Looks nicer. */
111
112 //To drop enemie before leave the screen
113 if (obj_pos.x<=16 || obj_pos.x+16>=Sector::get().get_width()){
114 carried_object->ungrab (*this, m_dir);
115 carried_object = nullptr;
116 }
117
118 else
119 carried_object->grab (*this, obj_pos, m_dir);
120 }
121 else { /* if (is_above_player) */
122 carried_object->ungrab (*this, m_dir);
123 carried_object = nullptr;
124 }
125 }
126}
127
128bool
129Owl::collision_squished(GameObject&)
130{
131 auto player = Sector::get().get_nearest_player(m_col.m_bbox);
132 if (player)
133 player->bounce (*this);
134
135 if (carried_object != nullptr) {
136 carried_object->ungrab (*this, m_dir);
137 carried_object = nullptr;
138 }
139
140 kill_fall ();
141 return true;
142}
143
144void
145Owl::kill_fall()
146{
147 SoundManager::current()->play("sounds/fall.wav", get_pos());
148 m_physic.set_velocity_y(0);
149 m_physic.set_acceleration_y(0);
150 m_physic.enable_gravity(true);
151 set_state(STATE_FALLING);
152
153 if (carried_object != nullptr) {
154 carried_object->ungrab (*this, m_dir);
155 carried_object = nullptr;
156 }
157
158 // start dead-script
159 run_dead_script();
160}
161
162void
163Owl::freeze()
164{
165 if (carried_object != nullptr) {
166 carried_object->ungrab (*this, m_dir);
167 carried_object = nullptr;
168 }
169 m_physic.enable_gravity(true);
170 BadGuy::freeze();
171}
172
173void
174Owl::unfreeze()
175{
176 BadGuy::unfreeze();
177 m_physic.set_velocity_x(m_dir == Direction::LEFT ? -FLYING_SPEED : FLYING_SPEED);
178 m_physic.enable_gravity(false);
179 m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right");
180}
181
182bool
183Owl::is_freezable() const
184{
185 return true;
186}
187
188void
189Owl::collision_solid(const CollisionHit& hit)
190{
191 if (m_frozen)
192 {
193 BadGuy::collision_solid(hit);
194 return;
195 }
196 if (hit.top || hit.bottom) {
197 m_physic.set_velocity_y(0);
198 } else if (hit.left || hit.right) {
199 if (m_dir == Direction::LEFT) {
200 set_action ("right", /* loops = */ -1);
201 m_dir = Direction::RIGHT;
202 m_physic.set_velocity_x (FLYING_SPEED);
203 }
204 else {
205 set_action ("left", /* loops = */ -1);
206 m_dir = Direction::LEFT;
207 m_physic.set_velocity_x (-FLYING_SPEED);
208 }
209 }
210}
211
212void
213Owl::ignite()
214{
215 if (carried_object != nullptr) {
216 carried_object->ungrab (*this, m_dir);
217 carried_object = nullptr;
218 }
219 BadGuy::ignite();
220}
221
222ObjectSettings
223Owl::get_settings()
224{
225 ObjectSettings result = BadGuy::get_settings();
226
227 result.add_text(_("Carry"), &carried_obj_name, "carry"); //, std::string("skydive"));
228
229 result.reorder({"carry", "direction", "sprite", "x", "y"});
230
231 return result;
232}
233
234/* EOF */
235